ThinkPHP数据库查询缓存不会自动失效,因框架未设计监听机制;save()、delete()等操作均不触发清理,必须手动通过打标签(Cache::tag)、捕获变更、精准清除来实现业务级缓存同步。

ThinkPHP 的数据库查询缓存不会自动失效,这不是配置漏了或驱动坏了,而是框架根本没设计这层监听逻辑——save()、delete()、事务提交、软删除,全都不会触发任何缓存清理动作。
为什么 cache() 写的缓存永远不会自己过期?
TP6 默认不开启任何自动缓存失效机制。你调用 UserModel::where('id', 1)->cache(3600)->find(),它只做两件事:生成一个基于 SQL 的 key(比如 md5("SELECT * FROM user WHERE id = 1")),然后把结果塞进 Redis 或文件里,设个 TTL。之后哪怕你改了这条记录,缓存还躺在那儿,直到自然过期或被手动删掉。
- 缓存键是静态生成的,不包含表版本、字段变更、关联状态等上下文
-
with()加载的关联数据,除非你在关联方法里显式写->cache(true),否则根本不进缓存 - 软删除字段(如
delete_time)变更后,缓存仍返回逻辑已删的数据,因为查询条件没变,key 没变
怎样让缓存跟着业务数据一起“动”起来?
核心思路不是等框架自动响应,而是把缓存生命周期和业务变更事件绑定。关键在三点:打标签、捕获变更、精准清除。
- 用
Cache::tag()给缓存打业务语义标签,比如Cache::tag('user_123')->set('profile', $data, 3600),而不是靠 key 命名硬匹配 - 在模型的
afterWrite钩子中判断哪些字段变了:$changed = $this->getChangedData(),若in_array('nickname', array_keys($changed)),就执行Cache::tag('user_123')->clear() - 关联表更新时,也要主动清对应 tag:Profile 更新后,不只是清
profile_456,还得清Cache::tag('user_123')->clear(),因为用户详情页可能缓存了带 profile 的完整数据 - 别在控制器里写
Cache::delete('user_123')—— key 很可能和实际写入的不一致(prefix、序列化、tag 封装都会影响)
cache() 方法传参错一个类型,TTL 就完全失效
这个坑在 TP3.2.3 就存在,到 TP6 依然没绕开:cache() 的第二个参数(过期时间)必须是整数秒,传字符串如 '30' 在 Redis 驱动下会被当成永久缓存(TTL = -1);传浮点数或 DateTime 对象则行为不可控,File 驱动直接忽略。
立即学习“PHP免费学习笔记(深入)”;
- 正确写法:
->cache(true, 600)或->cache('user_123', 600) - 错误写法:
->cache(true, '600')(字符串)、->cache(true, 600.0)(浮点)、->cache(true, new \DateTime('+10 minutes'))(对象) - TP6 中
Cache::remember()同样严格:Cache::remember('key', 600, $callback),第二个参数必须是 int,放错位置或类型,就用驱动默认值(通常是 3600 秒)
File 缓存的“过期”其实是假过期
File 驱动的过期检查依赖文件修改时间 + 进程内缓存预加载,没有后台清理进程。这意味着:缓存文件物理还在磁盘上,只是读取时会比对 filemtime() 和当前时间;高并发下多个请求同时发现过期,可能全都去查库再写缓存,造成雪崩;更麻烦的是,Swoole 环境下如果用了 SimpleCache(内存数组驱动),default_expire 根本无效,它压根不支持 TTL,只能靠重启 worker 或手动 Cache::delete()。
- 上线前务必确认生产环境用的是 Redis 驱动,而不是默认 File
- 用
Cache::handler()和Cache::getDriverName()实时验证当前生效的驱动,别只信 config 文件 - File 缓存适合开发调试,但不能用于对一致性敏感的场景,比如订单状态、库存数量
真正难的不是写几行 Cache::tag()->clear(),而是在复杂关联和多级缓存中,理清哪条数据变更该触发哪些 tag 清理——比如商品价格改了,要清商品详情、分类列表、搜索聚合、购物车摘要,这些 tag 往往分散在不同模型、不同服务里,靠人工维护极易遗漏。



















