Redis缓存雪崩本质是大量key同时过期导致请求穿透DB且无协调机制,解决关键是缓存重建串行化:用SETNX加锁确保仅一个线程查DB写缓存,其余等待或返回旧值;辅以本地缓存减少锁竞争,Redisson RLock更可靠,但需注意租期、异常处理与业务语义匹配。

Redis缓存失效时DB被打爆,本质是没做互斥重建
缓存雪崩本身不是Redis的问题,而是大量key同时过期后,所有请求穿透到DB,又没有协调机制,导致DB瞬间并发飙升。关键不在“防雪崩”,而在“让缓存重建串行化”——即只放行一个线程去查DB、写缓存,其余等待或重试。
SETNX + 过期时间是最轻量的互斥入口
用SETNX抢锁是最直接的方式:只有抢到锁的请求才去查DB;没抢到的,要么短暂休眠后重试,要么直接返回旧缓存(如果业务允许 stale data)。
-
SETNX cache:key:123 lock_value EX 30 NX—— 必须带EX和NX,避免死锁;过期时间建议略大于DB查询耗时(比如DB平均查2s,这里设5s) - 抢锁失败不等于缓存不存在,可能是别人正在重建,此时应
GET cache:key:123再判断是否已回填 - 别用
DEL + SET模拟SETNX,竞态下会覆盖他人锁 - 客户端不能自己维护锁状态,一切以Redis原子命令为准
本地缓存+Redis双重校验能减少90%以上的锁竞争
纯靠Redis锁在高并发下仍有开销,尤其当大量请求几乎同时到达。加一层进程内缓存(如Guava Cache、Caffeine或Python的lru_cache),可拦截绝大多数重复请求。
- 先查本地缓存,命中则直接返回(注意控制大小和TTL,避免脏数据)
- 未命中再走Redis锁流程;重建成功后,同步写入本地缓存和Redis
- 本地缓存TTL建议设为Redis TTL的1/3~1/2,提前触发预热,降低集中过期风险
- 不要把本地缓存当作兜底——它不解决分布式一致性,仅作性能缓冲
用Redisson的RLock比手写更稳,但要注意租期续期
手写SETNX容易漏掉异常清理、锁误删等问题。Redisson的RLock自带看门狗机制,自动续期,更适合生产。
- 必须用
lock.lock(30, TimeUnit.SECONDS)带租约参数,否则默认30秒,DB慢查询超时会导致锁提前释放 - 别在锁内做HTTP调用或长IO操作——网络抖动可能让看门狗失效,引发锁失效
- 务必配
unlock()在finally块中,且检查是否为当前线程持有锁(isHeldByCurrentThread()) - 集群模式下确认
Redisson配置了正确的ClusterServersConfig,否则锁可能只在单节点生效
真正难的不是加锁,而是判断“什么时候该等、什么时候该返回空、什么时候该降级”。业务语义决定了互斥粒度——是按key互斥,还是按业务维度(如用户ID、商品类目)聚合互斥。这点没法交给中间件自动推断。

















