ThinkPHP模型缓存默认不区分where条件,仅基于类名、方法名和SQL模板生成缓存键;需手动构造含参数的唯一缓存键(如cache(['user_list', $status, $type], 300))或改用Db查询控制缓存。

模型查询时 where 条件没生效,缓存却命中了
ThinkPHP 的模型缓存默认不区分 where 条件,只要模型名和方法名一样(比如都调用 UserModel::select()),就可能复用上一次的缓存结果。这不是 bug,是设计如此——缓存键默认只含类名 + 方法名 + SQL 模板,不含实际绑定参数。
实操建议:
- 显式开启条件缓存:在查询链中加
cache(true)并传入唯一缓存键,比如cache('user_status_1', 3600) - 避免直接对模型方法整体缓存,改用
Db::name('user')->where(...)->cache(...)->select(),更可控 - 如果用了
scope或动态where,务必手动拼缓存键,否则不同条件会互相污染
cache() 第二个参数设成 0 还是 null?
ThinkPHP 6.x 中,cache() 的第二个参数控制缓存有效期,但行为有差异:
- 传
0:表示「永久缓存」,实际写入缓存驱动时会用驱动的默认永有时长(如 Redis 不设 TTL) - 传
null:表示「使用配置里cache.default_expire的值」,通常是 3600 秒 - 传具体数字(如
600):单位是秒,明确过期时间
容易踩的坑:本地开发用 file 缓存时,0 看似永久,但重启服务或清缓存目录就丢了;生产用 Redis 时,0 才真可能长期存在,得配合 cache:clear 命令主动清理。
立即学习“PHP免费学习笔记(深入)”;
不同参数导致结果不同,但缓存键没变怎么办
典型场景:用户列表接口带 status 和 type 查询参数,你写了 UserModel::where('status', $status)->where('type', $type)->cache(true)->select(),结果所有参数组合都返回同一份缓存。
原因在于 cache(true) 自动生成的键没包含变量值。解决方式只有手动构造带参缓存键:
- 用字符串拼接:
cache('user_list_' . md5($status . '_' . $type), 300) - 用数组生成键(推荐):
cache(['user_list', $status, $type], 300),ThinkPHP 会自动序列化为稳定字符串 - 别依赖
__call魔术方法或静态方法封装里的隐式缓存,它们几乎都不处理参数差异
关联查询 + 缓存,为什么数据总是旧的
ThinkPHP 的关联预载入(with())本身不参与缓存键计算。哪怕你写了 UserModel::with('profile')->cache(true)->find(123),缓存键仍是 UserModel_find_123,不包含 profile 是否加载、是否加了额外条件等信息。
这意味着:
- 先查
with('profile')写入缓存,再查with('posts'),可能命中前者的缓存,但返回空posts - 关联模型自己也启用了缓存,但主模型缓存没失效机制,导致级联更新失败
- 解决办法:给每个关联组合配独立缓存键,例如
cache('user_with_profile_123', 600),而不是依赖cache(true)
最易被忽略的一点:模型事件(如 afterWrite)里清除缓存,必须连带清除所有可能涉及该记录的缓存键——包括带参数的、带关联的、甚至列表页的分页缓存,漏一个就会看到“明明改了,页面还是旧的”。


















