cache::remember() 缓存失效主因是键名设计不当、闭包含副作用、驱动限制未规避;需将查询变量全纳入键名、闭包仅限幂等查询、用tags批量管理、空结果显式缓存。

Cache::remember() 不是万能开关,用错键名、混入副作用、忽略驱动限制,缓存反而会失效甚至引发数据不一致。
Cache::remember() 为什么查不到缓存?
缓存没命中,八成是键名设计出了问题。Laravel 不会自动帮你“理解语义”,Cache::remember('users', 3600, ...) 这种泛化键名在多条件分页、带搜索参数的接口里必然冲突——不同用户、不同页码都往同一个键写,后写覆盖先写,读出来的永远是最后那个结果。
- 必须把影响查询结果的所有变量纳入键名:分页参数、排序字段、搜索关键词、时间范围都要拼进去,例如
'users_page_'.$page.'_sort_'.$sort.'_q_'.$q - 避免用高基数字段(如
$user->id)直接作键主体,除非配合Cache::tags()分组管理 - 键名里别塞 JSON 字符串或未转义的特殊字符,Redis 对 key 的长度和字符有严格限制,超长或含控制字符会导致静默失败
- 开发环境默认用
file驱动,它不支持原子性操作,高并发下可能读到过期值或写丢失——上线前务必确认CACHE_DRIVER=redis
闭包里能写日志、发邮件、更新数据库吗?
不能。Cache::remember() 的闭包逻辑必须是幂等的——也就是“执行一次和执行十次结果完全一样”。一旦你在里面调用了 Mail::send() 或 $user->increment('login_count'),缓存命中时这些操作就全跳过了,行为完全不可预测。
- 只允许纯查询:Eloquent
get()、first()、count(),或DB::table()->select()->where()->get() - 禁止任何写操作、事件触发、外部 HTTP 调用、文件写入
- 如果业务确实需要“查完顺手记一笔”,拆成两步:先用
Cache::remember()查,再在缓存未命中时单独执行副作用逻辑
缓存失效了,怎么批量清掉一组数据?
靠拼接键名硬删是自找麻烦。Cache::tags() 是唯一靠谱的批量管理方式,但它只在 Redis 和 Memcached 驱动下有效,file 或 database 驱动直接不支持,调用会抛出 BadMethodCallException。
- 写入时打标签:
Cache::tags(['posts', 'featured'])->put('post_123', $data, 3600) - 更新文章状态后,一键刷新:
Cache::tags(['posts', 'featured'])->flush() - 不要在模型事件里无差别调用
flush(),比如User::updating()中清['users']标签,可能误伤其他模块缓存 - 标签名要收敛、有业务含义,避免用动态 ID(如
['user_'.$id]),否则无法按角色/状态等维度批量清理
最常被忽略的一点:空结果也要缓存。查不到数据时如果不显式存个 null 或空集合,下次请求还会原样打到数据库,形成缓存穿透。得写成 Cache::remember($key, 60, fn() => User::find($id) ?? null),并确保业务层能正确处理 null 值。











