缓存预热应在应用启动后、处理请求前的单次初始化阶段进行,推荐使用 artisan 命令配合部署脚本执行;避免在 appserviceprovider::boot()、路由、中间件或配置文件中实现,应选用 cache::put() 而非已废弃的 cache::forever() 或带回调逻辑的 cache::rememberforever()。

缓存预热该在 Laravel 的哪个生命周期做
不能在 AppServiceProvider::boot() 里直接塞大量缓存写入逻辑,尤其涉及数据库查询或外部 HTTP 调用时——它会在每次请求都执行,反而拖慢响应。真正适合的时机是应用启动后、但尚未处理任何请求前的「单次初始化」阶段。
推荐做法:用 Artisan 命令 + php artisan optimize:clear 后触发预热,再配合部署脚本自动运行。这样既可控,又避免污染请求生命周期。
- 不要在
routes/web.php或中间件里写预热逻辑,会重复执行 - 避免在
config/cache.php中通过闭包加载数据,Laravel 不会执行其中的副作用代码 - 如果用 Horizon 或 Queue Worker,需额外确保预热命令在 worker 启动前完成,否则队列任务可能读到空缓存
用 Cache::rememberForever() 还是 Cache::put()
选 Cache::put()。因为 Cache::rememberForever() 底层仍走 remember() 逻辑,会注册一个“未命中时回调”,而预热的目标是确保数据一定存在、且不带条件逻辑——多一层抽象反而增加不可控性。
另外注意:Redis 驱动下 Cache::put() 默认无过期时间(TTL=0),等效于“永久”,但实际依赖 Redis 配置是否启用 maxmemory-policy;如果用 File 或 Database 缓存,则必须显式传 null 或 0 表示永不过期。
-
Cache::put('hot_user_count', $count, 3600)—— 显式设 TTL,适合有明确时效要求的数据 -
Cache::put('site_config', $config, null)——null表示永不过期(File/Database 驱动必需) - 别用
Cache::forever(),它已被 Laravel 9+ 标记为废弃
预热时遇到 Connection refused 或 SQLSTATE[HY000] [2002]
这是典型环境错位:预热命令运行在 CLI 环境,而你的数据库配置可能依赖 .env 中的 DB_HOST,但 Docker 或某些部署环境里 CLI 容器无法解析 localhost 指向 DB 容器(Web 请求能通是因为 Nginx + PHP-FPM 共享网络栈)。
解决方法不是改代码,而是统一配置来源。检查 config/database.php 是否硬编码了 'host' => '127.0.0.1',应改为 env('DB_HOST', 'localhost'),并在部署时确保 CLI 环境也加载了正确的 .env(比如用 php -d variables_order=EGPCS artisan cache:warmup 避免 CLI 忽略 $_ENV)。
- 用
php -i | grep 'Loaded Configuration File'确认 CLI 加载的是哪个 php.ini - 运行
php artisan tinker --execute="echo config('database.connections.mysql.host');"验证配置是否生效 - Redis 同理,
CACHE_HOST在 CLI 下也要可访问,别只测试 Web 请求通就认为没问题
如何判断预热是否真的生效
别只看命令没报错。最简单的方式是预热后立刻用 php artisan tinker 查缓存内容,并对比首次读取耗时:
php artisan tinker
> Cache::has('hot_posts'); // 应返回 true
> microtime(true) - (function() { Cache::get('hot_posts'); return microtime(true); })(); // 对比首次 get 耗时是否接近 0.0001s
更稳妥的做法是在预热命令末尾加一行日志写入 storage/logs/warmup.log,记录键名、大小、时间戳,方便上线后抽样核对。
- Redis 用户可用
redis-cli KEYS "hot_*"快速确认键是否存在(生产禁用 KEYS,仅调试) - 别依赖浏览器 DevTools 的 Network 面板看缓存命中——那是 HTTP 层,和 Laravel Cache 是两回事
- 如果用了 Taggable 缓存(如
Cache::tags(['posts'])->put(...)),预热后务必验证Cache::tags(['posts'])->get(...)能正确读出,Tag 在不同驱动下行为差异大
缓存键命名是否带环境前缀(比如 prod:hot_users)、序列化方式是否一致(尤其是用了 msgpack 或 igbinary)、以及预热命令是否在多台服务器上都执行到位——这些细节一旦漏掉,线上就只能看到“明明预热了却还是慢”。











