thinkphp redis缓存预热必须通过cli脚本主动执行,禁止在web请求或appserviceprovider中同步加载;需用setnx加锁防多实例重复写入,统一key前缀,区分关键路径全量预热与增量补全,并通过redis-cli和命中率验证实效性。

ThinkPHP 用 Redis 做缓存预热,不是配好 cache.php 就自动生效的——必须主动把数据写进去,且得避开上线后多个进程争抢、重复加载、漏掉关键路径这些坑。
预热脚本必须用 CLI 模式执行,不能走 Web 请求
Web 请求里做预热(比如在控制器里判断 Cache::has() 再 Cache::set())在高并发下会直接触发缓存穿透+数据库击穿。哪怕加了锁,用户请求一涌而上,照样可能同时查库、同时写缓存。
- 预热逻辑必须剥离到独立 PHP CLI 脚本中,例如
php think cache:preheat - 该命令应由部署流程(如 GitLab CI/CD 或 Ansible)在发布完成后立即调用,确保服务启动前缓存已就位
- 禁止在
AppServiceProviders的boot()中同步执行全量预热——会拖慢框架启动,甚至超时失败
用 Redis SETNX 加锁防重复加载,尤其多实例部署时
当 ThinkPHP 部署在多台机器上,且都配置了相同 crontab 或都监听了发布事件,不加锁就会导致同一份商品列表被反复查库、反复写 Redis,浪费资源还可能因序列化不一致造成脏数据。
- 在预热脚本开头用
$redis->set('cache:preheat:lock:hot-products', time(), ['NX', 'EX' => 60])获取 60 秒锁 - 返回
false表示锁已被占,直接退出(不重试、不 fallback 到查库) - 锁成功后才执行
Db::name('product')->where('is_hot', 1)->select()等查询 +Cache::set()写入 - 务必用
Redis::OPT_PREFIX统一前缀,避免和运行时缓存 key 冲突,例如设为tp6:preheat:
区分「关键路径」和「增量补全」,别一股脑全量刷
电商首页 banner、热销榜、类目树这类数据访问频次极高、更新极少,适合发布后立刻全量预热;而用户个人中心数据、搜索词热度榜这种,更新频繁、冷热不均,全量刷是浪费,还容易过期失效。
- 关键路径预热:只加载
SELECT * FROM product WHERE is_hot = 1 LIMIT 100这类明确、稳定、低变更的数据 - 增量补全:用定时任务(如每小时)跑
SELECT id FROM product WHERE updated_at > DATE_SUB(NOW(), INTERVAL 1 HOUR),只刷新最近改过的条目 - 避免对
user_profile类 key 做预热——它天然不具备“热点”属性,预热反而挤占内存、拉低命中率
验证预热是否真生效,别只看日志“完成”二字
脚本输出 预热完成!共写入 237 条 不代表数据真的能被业务代码读到。常见断点在序列化方式、过期时间、驱动配置不一致。
- 用
redis-cli直连确认 key 存在:redis-cli -h 127.0.0.1 -p 6379 keys "tp6:preheat:hot-product:*" - 检查值是否可反序列化:
redis-cli get "tp6:preheat:hot-product:1001"应返回 JSON 字符串,而非a:3:{...}(那是 PHP serialize 格式,ThinkPHP 默认用 JSON) - 确认
config/cache.php中'redis'store 的'serialize'选项没被意外设为true——这会导致Cache::get()读出来是乱码数组 - 上线后紧盯
Cache::hitRate()(需开启监控扩展)或 Redis 的INFO stats中keyspace_hits/keyspace_misses比值,首小时命中率低于 85% 就要查漏
最常被忽略的是:预热脚本里用的 Redis 连接参数(host/port/password/select)和线上 cache.php 配置不一致,结果数据写进测试库,业务却连着生产库——两边根本不在一个地方。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











