Db::cache() 必须在查询执行前调用,如 Db::name('user')->cache(3600)->find(1);缓存更新需主动清除,字段缓存需关闭或刷新,OPcache/代理缓存需单独处理。

Db::cache() 不生效导致查询结果始终旧值
ThinkPHP 的 Db::cache() 默认不自动缓存查询结果,必须显式调用且位置关键。常见错误是把 cache() 放在 find() 或 select() 之后,比如 Db::name('user')->find()->cache(3600) —— 这完全无效,因为 find() 已执行并返回数组,后续链式调用无意义。
正确写法必须让 cache() 在查询执行前介入:
Db::name('user')->cache(3600)->find(1)Db::name('user')->where('status', 1)->cache('user_active_list', 600)->select()- 若需自定义 key,务必全局唯一:避免用
md5(serialize($where))动态生成,改用稳定字符串如'stats_daily_' . date('Y-m-d')
特别注意:TP6.1+ 中 cache(0) 表示永不过期,但存在驱动兼容 bug,建议传 null 或大整数(如 31536000)。
缓存更新时未同步清除,导致统计数据滞后
请求统计类场景(如“今日访问量”“接口调用频次”)常犯的错误是:只写数据库,不删/不更新对应缓存键。例如用户调用 /api/report,后端更新了 stat_day 表,但缓存键 'report_today' 仍有效,前端永远读到旧值。
立即学习“PHP免费学习笔记(深入)”;
必须在数据变更后主动清理或刷新:
- 清除缓存:
cache('report_today', null)或Cache::delete('report_today') - 带标签更安全:
Cache::tag('report')->set('today', $data, 3600),更新时只需Cache::tag('report')->clear() - 标签名必须全小写、无空格和冒号:
'report'✅,'Report'❌,'report:today'❌
如果用了 Redis 驱动,确认没和其他服务共用同一 DB——Cache::clear() 在 TP5.0 默认执行 flushDB,会清空整个 Redis 实例。
字段缓存锁定旧表结构,新增统计字段查不到
前后端分离项目频繁调整统计口径(如新增 pv_24h、uv_week 字段),但 ThinkPHP 默认开启字段缓存,导致 Db::name('stat')->field('pv_24h, uv_week')->select() 静默丢弃新字段,甚至不报错。
开发阶段最省事的解法是关掉字段缓存:
- 修改
config/database.php,设'fields_cache' => false - 上线后若需性能,每次执行
ALTER TABLE stat ADD pv_24h INT后,必须运行:php think clear或手动删runtime/schema/stat.php - 代码中可强制刷新:
\think\facade\Db::table('stat')->getFieldsType()
别依赖“改完 SQL 就完事”——字段缓存文件不删,框架就永远按旧结构解析 SQL。
OPcache 或反向代理缓存了接口响应,改代码也不生效
统计接口(如 /api/v1/stats)返回 JSON,但 Nginx 的 fastcgi_cache 或 Cloudflare 可能把它当静态资源缓存了。现象是:加了 dump()、改了 SQL、甚至重启 PHP-FPM,curl 请求结果仍不变。
快速验证和定位:
- 用带唯一参数的请求测试:
curl "http://localhost/api/v1/stats?_t=$(date +%s)",若带参数变、不带不变,就是代理层缓存 - 查 Nginx 配置:
grep -r "fastcgi_cache" /etc/nginx/,临时注释相关行并nginx -s reload - 确认 PHP OPCache:
opcache.validate_timestamps = 1(开发必开),否则改完控制器代码也不会重编译
线上环境别靠反复删 runtime/ 解决——真正卡住的是 OPcache 内存或 CDN 节点,得从源头关或清。
统计类缓存最易被忽略的点:不是“该不该缓存”,而是“谁负责失效”。数据库改了、定时任务跑了、API 被调用了——这些动作都必须明确触发缓存清理,不能假设过期时间一到就万事大吉。尤其跨服务部署时,一个 Cache::tag('stats')->clear() 调用可能因 Redis 连接配置不同而静默失败。



















