缓存失效指“存了但读不到”或“读到了却过期/为空”,核心问题常在驱动配置、执行链路或键名管理;需先确认cache_driver=redis生效、手动验证redis写入与前缀匹配,并区分自然过期、被动失效及穿透/击穿场景。

缓存失效不是“没存进去”,而是“存了但读不到”或“读到了却过期/为空”。核心问题往往不在业务逻辑,而在驱动配置、执行链路或键名管理上。先确认驱动是否真在跑,再查数据是否真写进去了。
确认缓存驱动已生效
很多问题卡在这一步:.env 写了 CACHE_DRIVER=redis,但实际还是走 file 驱动。
- 运行 php artisan tinker --execute="echo config('cache.default');",输出必须是 redis;如果不是,检查 config/cache.php 中
'default' => env('CACHE_DRIVER', 'file')是否被正确加载,再执行 php artisan config:clear && php artisan config:cache - 用 Cache::store('redis')->put('test_key', 'ok', 60) 手动写入,然后用 redis-cli -h ${REDIS_HOST} -p ${REDIS_PORT} get "laravel_cache:test_key" 直接查 Redis,确认值存在且未过期
- 注意:Laravel 默认复用 database.redis.default 连接,若 session 或 queue 占用了 DB 0,建议在 config/database.php 新增专用 cache 连接(如 database=1),并在 config/cache.php 的 redis store 中指定
'connection' => 'cache'
排查 remember() 缓存未写入的常见断点
remember() 是链式调用,中间任何打断都会让闭包不执行,等于跳过了缓存写入。
- 删掉所有 dd()、dump()、toSql() —— 它们会终止后续方法调用,缓存根本不会生成
- 闭包内引用了外部变量(比如 $request、$status)但没加 use($request, $status),PHP 可能静默返回空数组,缓存里存的就是空值
- 缓存键含非法字符(如空格、/、?、中文),Laravel 会静默失败;推荐统一用 md5($url) 或 sha1("prefix_{$id}_{$status}") 生成键名
验证缓存是否真的被命中
别只看结果对不对,要看它是不是从缓存来的。
- 在闭包里加日志:Log::info('Cache miss: loading fresh data');,如果每次请求都打这条日志,说明缓存压根没生效
- 用 Cache::store('redis')->get('your_key') 在 tinker 里手动查,确认键是否存在、值是否非空、TTL 是否合理
- 注意:Redis 的 key 实际是 前缀 + 键名(如 laravel8_:user:123),查的时候要带前缀;前缀来自 config/cache.php 的
'prefix' => env('CACHE_PREFIX', 'laravel8_')
区分缓存失效与缓存穿透/击穿
失效 ≠ 没缓存。三种情况处理方式完全不同:
- 自然过期:TTL 到期后自动不可读,属于预期行为;只需确保 TTL 设置合理(如耗时查询不低于 300 秒)
- 被动失效:模型更新后缓存未清理,需在模型中监听 saving/deleted 事件,显式调用 Cache::forget('key') 或 Cache::tags(['users'])->flush()
- 穿透/击穿:高并发下大量请求同时发现缓存为空,一齐打到数据库;应加互斥锁(如 Cache::lock())或设二级缓存兜底











