ThinkPHP 模型查询默认不缓存,需手动调用cache()方法;关联查询和聚合函数缓存支持有限,应拆分处理;缓存键易冲突,建议用自定义key;模型增删改后须手动清理缓存以保证一致性。

ThinkPHP 模型查询默认不缓存,必须显式开启
TP6 的 Db 和 Model 查询默认完全绕过缓存,哪怕你配置了 Redis 或 File 缓存驱动,find()、select() 这些方法也不会自动写入或读取缓存。这不是 bug,是设计选择——框架把缓存决策权交给开发者,避免隐式行为导致数据陈旧。
实操建议:
- 缓存必须手动调用
cache()方法,且需在查询链路中「提前」插入,比如UserModel::where('id', 1)->cache(3600)->find() -
cache()接收两个参数:cache($key, $expire),其中$key可省略(框架自动生成),但一旦指定就必须全局唯一,否则多个查询共用同一 key 会相互覆盖 - 如果模型启用了软删除(
use SoftDelete),缓存不会自动感知delete_time字段变化,查到的可能是逻辑删除后的“脏数据”
cache() 方法对关联查询和聚合函数的支持很有限
当你在模型中使用 with() 加载关联、或调用 count()/sum() 等聚合方法时,cache() 很可能失效或缓存内容异常。原因是 TP 的缓存键生成逻辑只基于主表 SQL 片段,忽略关联子查询和函数上下文。
常见错误现象:
-
UserModel::with('profile')->cache(600)->select()缓存命中后,profile关联数据为空或为旧值 -
UserModel::where('status', 1)->count()->cache(300)报错Call to a member function cache() on int,因为count()返回的是标量,不是 Query 对象
实操建议:
- 关联数据缓存应拆开:先缓存主模型列表,再单独缓存各关联模型(如
ProfileModel::where('user_id', $ids)->cache(600)->select()) - 聚合结果需自行封装,例如用
Cache::remember('user_active_count', 600, function () { return UserModel::where('status', 1)->count(); })
缓存键冲突和过期策略容易被低估
TP 默认用查询 SQL 的 MD5 值作缓存 key,看似安全,但实际极易冲突:SQL 中带 PHP 变量拼接、不同环境 SQL 格式微调(空格、换行)、甚至数据库配置差异(如前缀不同),都会导致 key 不一致,缓存形同虚设。
性能与兼容性影响:
- 高并发下大量相同查询反复生成 MD5,CPU 开销明显;用自定义字符串 key(如
'user_list_status_1')可跳过这步 - Redis 缓存驱动下,
cache(0)表示永不过期,但 TP6.1+ 对 0 的处理有 bug,建议明确传null或一个大数值(如31536000) - File 缓存驱动在 Windows 下对长 key 名支持差,超过 255 字符可能静默失败,务必控制自定义 key 长度
模型事件和缓存一致性必须手动维护
TP 不提供“写穿透”或“写失效”自动机制。你在 save() 或 delete() 后,对应缓存不会自动清理——这是最常被忽略的一环,也是线上数据不一致的主因。
实操建议:
- 在模型的
afterSave()和afterDelete()钩子中主动删除缓存,例如:Cache::delete('user_info_' . $this->id) - 避免用通配符批量删(如
Cache::tag('user')->clear()),TP 的 tag 功能在非 Redis 驱动下不可靠,且 File 驱动根本不支持 tag - 如果业务允许轻微延迟,可改用“过期即失效”策略:给列表类缓存设较短过期时间(如 60 秒),比强一致性更稳
缓存不是开关,是需要和模型生命周期对齐的一套契约。漏掉一次 Cache::delete(),就可能让前端展示出三天前的用户头像。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











