互斥锁是laravel高并发下防缓存击穿的核心方案:先查缓存,未命中则用cache::lock抢锁,抢到后双重检查再查库、写缓存并释放,失败则短时重试或降级返回。

在 Laravel 高并发场景中,缓存击穿的核心问题是:某个热点 key 刚过期,大量请求同时发现缓存缺失,全部涌向数据库,造成瞬时压力飙升。互斥锁(Mutex Lock)是最直接、最可控的应对方案——它确保同一时刻只有一个请求能穿透到数据库,其余请求等待并复用新写入的缓存。
用 Cache::lock 实现轻量级互斥控制
Laravel 原生支持基于 Redis 的分布式锁,无需额外包。关键在于“先查缓存 → 未命中则抢锁 → 抢到后再查一次缓存(双重检查)→ 查无再查库 → 写缓存 → 释放锁”这一闭环逻辑:
- 调用 Cache::lock('user_profile_lock_'.$userId, 10) 获取带超时的锁,10 秒防死锁
- 获取锁成功后,**必须再次检查缓存是否存在**(防止其他请求已写入),避免重复查询
- 若确认仍为空,执行数据库查询,并用 Cache::put('user_profile_'.$userId, $data, 3600) 写入带 TTL 的缓存
- 主动调用 $lock->release();若未手动释放,Laravel 会在超时后自动清理
处理锁竞争失败的合理降级策略
不是所有请求都能立刻拿到锁,需避免无限重试或线程阻塞:
- 获取锁失败时,不立即报错,而是 sleep(20–100ms) 后重试 1–2 次,给前序请求留出写缓存时间
- 若重试仍失败,可返回上一版缓存(如有)、兜底空值(如 Cache::get('key', ['status' => 'loading'])),或走本地默认数据
- 切忌在锁外做耗时操作(如日志记录、HTTP 调用),否则延长锁持有时间,拖慢整体响应
结合 remember 与锁提升代码健壮性
把锁逻辑封装进业务方法,而非裸写在控制器里。推荐与 Cache::remember 协同使用:
- 先用 Cache::remember('user_'.$id, 3600, fn() => ...) 尝试读缓存并自动回源
- 但在回源闭包内部,嵌入锁逻辑,即“remember 回源时才加锁”,避免每次读都触发锁开销
- 示例结构:return Cache::remember("profile_{$id}", 3600, function () use ($id) { return $this->loadProfileWithLock($id); });
注意锁粒度与键名设计
锁不是越细越好,也不是越粗越稳,要平衡安全与性能:
- 锁 key 应绑定具体业务维度,比如 'lock:post_view_count:'.$postId,而非全局锁 'cache_rebuild'
- 避免用用户输入直接拼接锁名,需过滤或哈希(如 md5($userId . '_profile'))防止注入或冲突
- Redis 锁底层依赖 SETNX + EXPIRE,Laravel 会自动保障原子性,但务必确认 cache 配置中 driver 是 redis 且连接正常











