缓存不会自动失效是设计使然,因remember()仅读写缓存而不监听数据库变更;需显式用标签、事件监听或版本化键名实现同步更新。

闭包内执行数据更新操作,缓存不会自动失效——这不是 bug,而是设计使然。Laravel 的 remember() 只负责“读缓存 + 写缓存”,它不监听、也不感知数据库是否被修改,更不会主动追踪模型状态变化。
为什么更新数据后缓存还有效?
因为 remember 的缓存键(如 md5('select * from posts where status = ?'))和数据库变更之间没有绑定关系。哪怕你用 Post::where('id', 1)->update(['title' => 'new']) 改了数据,缓存里那个旧的查询结果依然存在,下次调用 Cache::remember('posts_active', ...) 还会原样返回。
- 模型事件(
saving/updated)不会被 remember 自动捕获 - 批量更新(
update())、原生 SQL、队列任务、外部脚本改库,全都不触发任何缓存清理 - remember 生成的键是哈希值,调试时看不出对应哪条业务逻辑,难以定位清理点
怎么让缓存跟着数据一起更新?
必须显式声明依赖关系,不能指望框架自动推断。推荐以下三种方式:
-
用缓存标签(tags)分组管理:写缓存时打标,更新时按标清除
例如:Cache::tags(['post', 'post:123'])->put('post:123:detail', $data, 3600);更新后调用Cache::tags(['post:123'])->flush() -
在模型中监听事件手动清键:在
Post::saved或Post::deleted回调里,构造相同键名并Cache::forget(...)
注意:键名必须严格一致,建议把关键参数(如 ID、状态)写进键里,比如'post_detail_'.$post->id -
键名嵌入版本号或时间戳:把缓存键和模型的
updated_at关联起来
例如:$key = 'post:'.$post->id.':v'.strtotime($post->updated_at);,这样每次更新都会自然产生新键,旧键自动“过期”不用清理
哪些操作会让缓存失效逻辑出错?
以下情况看似在更新数据,实则绕过了缓存失效机制:
- 用
DB::table('posts')->where(...)->update(...)—— 不触发模型事件 - 用
Post::where(...)->increment('view_count')—— saved 事件不触发 - 在 remember 闭包里调用了
Mail::send()或Log::info()—— 闭包应只做查询,副作用会导致行为不可控 - 缓存驱动设为
file却用了tags()—— 静默失败,根本不清
一个安全的实践模板
控制器中缓存查询:
$posts = Cache::tags(['posts', 'list'])->remember('posts_active_list', 3600, function () {
return Post::where('status', 1)->get();
});
模型中响应更新:
protected static function booted()
{
static::updated(function (Post $post) {
Cache::tags(['posts', 'list', 'post:'.$post->id])->flush();
});
}
这样既保证列表缓存可批量清理,又支持单条记录精准失效。


















