ThinkPHP关联缓存不会自动失效是设计使然:with()不启用缓存,须在关联方法中显式调用cache();save()/delete()不触发关联缓存清理,需手动清除或打标签;软删除、事务、驱动降级等场景均需针对性处理。

ThinkPHP 的关联缓存不会自动失效,这不是 Bug,是设计使然——框架不监听关联表变更,也不在 save() 或 delete() 时主动清理关联缓存键。
with() 查询后缓存没更新?先确认你真用了 cache()
很多人以为 with('profile') 就会缓存关联结果,其实它只是预加载语法糖,底层仍是实时查库。真正启用缓存必须显式调用 cache() 方法。
- 错误写法:
User::with('profile')->find(1)→ 每次都查库 - 正确写法:在模型关联方法里加
->cache(true),或手动链式调用->cache('user_profile_1') - 更安全的写法:用数组键
->cache(['user_profile', $this->id]),避免 key 冲突和哈希截断问题 - 注意:如果关联方法返回的是
hasOne但主模型未实例化(比如用where()->find()前就调用 with),$this->id是 null,缓存 key 会变成['user_profile', null],导致全量命中失败
关联数据改了,缓存却没清?别指望 afterSave 自动处理
afterSave 钩子只对当前模型生效,它不知道你前端哪个接口读了哪条关联缓存,更不会递归清 profile 对应的 user_profile_1。
- Profile 表更新后,必须手动清除对应缓存:
Cache::delete('user_profile_1')或Cache::tag('user_profile')->clear() - 打标签的前提是在关联定义里写了
->tag('user_profile'),否则只能靠 key 命名规则硬删 - 事务中更新 Profile 后立即清除缓存?危险!事务若回滚,缓存已删但数据还原,造成短暂不一致;建议把缓存清除放到事务外、且仅在 commit 成功后执行
软删除 + 关联缓存 = 隐形脏数据
开启软删除后,delete_time 字段被设值,但关联缓存里还存着这条“已删”记录,with() 查出来依然可见。
立即学习“PHP免费学习笔记(深入)”;
-
cache(true)不感知delete_time变更,也不会响应restore() - 临时方案:查询关联时加
->whereNull('delete_time')过滤,但缓存 key 若没体现这个条件,仍可能命中带脏数据的旧缓存 - 根治办法:把软删除状态纳入缓存 key,例如
->cache(['user_profile_active', $this->id]),并在更新delete_time时同步删掉该 key
Memcached/Redis 缓存看似生效,实际却走 file 回退
ThinkPHP 在连接 Memcached 或 Redis 失败时默认静默降级到 file 驱动,你看到 Cache::get() 有返回,不代表真走的是内存缓存。
- 验证是否真连上:在配置里把
host改成一个不存在的地址(如127.0.0.9),再执行Cache::set('test', 'ok', 60)—— 如果仍不报错,说明已降级 - 检查
config/cache.php中type是memcached(不是memcache)或redis,且driver值与CACHE_DRIVER环境变量一致 - Redis 认证失败、DB 选错、密码为空都会导致写入静默失败;用
redis-cli -a your_pass KEYS cache_*直接查服务端有没有 key 存进去
关联缓存最易被忽略的点是:它和主模型缓存完全解耦,既不共享生命周期,也不共用失效逻辑。哪怕你给主模型加了 cache(true),关联数据照样每次重查——除非你明确告诉它“这里也要缓,且这个 key 要这么算”。



















