缓存键必须包含全部搜索参数哈希值,如search_result_+md5(http_build_query($_get));tag需手动维护索引;分页应缓存total和data而非paginator对象;redis宜用统一ttl与allkeys-lru策略。

缓存键设计必须包含搜索参数的完整哈希
搜索功能的结果集不能用固定键(如 search_cache),否则不同关键词会互相覆盖。真实场景中,用户输入 keyword=php&sort=updated&limit=20,缓存键就得体现这些变量的组合变化。
实操建议:
- 用
md5(http_build_query($_GET))生成键名,比拼接字符串更安全(避免特殊字符、空格、顺序敏感问题) - 加业务前缀,比如
search_result_+ 哈希值,方便后期清理或监控 - 不要把分页偏移量(
offset)单独做键——它和limit共同决定数据范围,必须一起参与哈希 - 注意:ThinkPHP 的
Cache::get()不校验键类型,若传入数组会静默失败,务必确保键是字符串
使用 Tag 缓存时需手动维护标签生命周期
ThinkPHP 支持用 tag 对多个缓存项打标(如所有“用户搜索”相关缓存都打上 search_user 标签),但框架不会自动更新标签内容——你删了某条缓存,对应标签里的索引仍存在,导致 Cache::tag('search_user')->clear() 失效。
实操建议:
- 每次写入缓存时,显式调用
Cache::tag('search_user')->set($key, $data, $expire) - 修改或删除某次搜索结果,除了删
$key,还得用Cache::tag('search_user')->rm($key)同步标签索引 - 避免在高并发下对同一标签频繁
clear(),可能引发缓存雪崩;改用按需逐条rm()更稳妥 - Tag 功能依赖缓存驱动支持(File/Redis 都行,但 Apcu 不支持),检查
cache.php配置里的type是否兼容
分页数据不能只缓存当前页,要缓存「查询上下文」
直接缓存 Db::table('article')->where(...)->paginate(10) 返回的对象,会导致后续翻页拿不到总条数或分页器失效——因为 ThinkPHP 的 Paginator 对象含运行时状态,序列化后反序列化会丢失部分方法和连接上下文。
实操建议:
- 缓存两样东西:
total(总数)和当前页的data数组,而不是整个Paginator实例 - 构造分页器时,用
LengthAwarePaginator手动传入$data和$total,绕过数据库查询 - 如果用的是 ThinkPHP 6.0+,
paginate()的第三个参数可传入预计算的$total,此时只需缓存这个数字 + 当前页数据 - 注意:
count()查询本身也应缓存(键为search_count_+ 哈希),否则每次翻页都要查总数
Redis 驱动下注意 TTL 设置与内存碎片
搜索缓存通常设置较短过期时间(如 300 秒),但 Redis 在高频写入下可能因 key 过多、TTL 随机分布,导致内存碎片升高、响应延迟突增。
实操建议:
- 避免给每个搜索结果设独立 TTL,改用统一过期策略:先写入,再用
EXPIREAT命令批量设置相近的过期时间点(例如全部设为当前时间 + 300 秒的整数倍) - 启用 Redis 的
maxmemory-policy为allkeys-lru,防止冷搜索缓存长期占内存 - 定期用
redis-cli --scan --pattern 'search_*' | wc -l检查 key 数量,超 10 万要考虑加二级缓存(如本地 File 缓存热词) - ThinkPHP 的
redis缓存配置里,host和port错误时默认静默降级到 file,得在日志里搜redis connect failed才能发现
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











