必须用 cache::remember() 而非 get/put 实现查询缓存,因其内置原子锁防击穿、闭环回源逻辑更安全;需搭配 redis 驱动、标准化键名、主动失效及分级 ttl 才有效。

生产环境直接用 Cache::remember() 缓存数据库查询结果,但必须搭配 Redis 驱动 + 合理键名 + 主动失效,否则容易缓存错乱或雪崩。
为什么不能只用 Cache::get() 和 Cache::put() 做查询缓存
这两个方法是纯原子操作,不处理“查不到就查库再存”的逻辑闭环。手动写容易漏掉异常分支、重复执行闭包、或在并发下多次写入相同键(尤其是高并发搜索页)。Cache::remember() 内部已用原子锁(Redis 下为 SETNX)保障单次回源,更安全。
- 没封装回源逻辑:你得自己判断
Cache::get()是否为空,再查 DB、再put(),三步缺一不可 - 并发风险:多个请求同时发现缓存缺失,会一起执行回调,造成 DB 压力突增(缓存击穿)
- 过期时间硬编码分散:
put()的秒数要和业务逻辑耦合,难统一维护
Cache::remember() 的正确调用姿势
它不是“拿来就快”,关键在参数设计和上下文控制:
- 键名必须含业务标识和可变参数的标准化哈希,例如搜索场景:
search:posts:'.md5($keyword.$page).',避免空格、大小写、特殊符号污染键名 - TTL 要按数据敏感度分级:用户权限类设 60 秒,分类列表设 3600 秒,站点配置可设 86400 秒
- 回调函数里禁止写日志、发邮件等副作用操作——缓存未命中时可能被多次执行
- 不要在 Eloquent 模型事件(如
saving)里调用remember(),会触发递归缓存或死锁
缓存失效必须主动,不能只靠过期
过期是兜底机制,不是清理策略。等 TTL 到期才刷新,意味着脏数据会持续存在整段时间。真实业务中,数据变更点清晰,应立刻清除相关缓存:
- 更新文章后,执行
Cache::forget('posts:'. $id)和Cache::forget('posts:latest:5') - 用 Redis 标签(
Cache::tags(['posts']))批量清理,但仅限 Redis/Memcached 驱动支持 - 避免
Cache::flush()—— 它清空全部缓存,影响无关模块,生产禁用 - 如果用 file 或 database 驱动,标签不可用,只能靠键名前缀 + 手动遍历删除(不推荐,性能差)
驱动选错等于白配
file 驱动在生产环境缓存数据库查询,基本等于没缓存:
- 每次读写都要打开/关闭文件句柄,磁盘 IO 成瓶颈,QPS 上不去 database 驱动看似“省事”,实则把缓存压力转嫁给数据库,反而加重主库负载
- Redis 是唯一能支撑高并发读写的合理选择:毫秒级响应、原生支持 TTL、原子操作、支持标签
- 确认
CACHE_DRIVER=redis已写入.env,且config/cache.php中'redis'连接配置指向可用实例
最常被忽略的是缓存键的语义一致性——同一份数据在不同地方用不同键名缓存,或键名拼接了未过滤的用户输入,会导致缓存碎片化和命中率暴跌。别省那几行标准化逻辑。











