原子网络科技 原子网络科技

Redis缓存穿透、击穿和雪崩的完整解决方案:布隆过滤器、互斥锁以及随机过期时间的实际应用

a169kai 2026年11月14日 · 阅读约 3 分钟

在高并发系统中,缓存失效通常不会慢慢出现,而是一下子就把压力传导到数据库上去了。穿透、击穿和雪崩这三种情况看起来很像,但是它们的原因不一样,解决的方法也不同。

拦截:阻止不存在的查询

异常流量一直查询不存在的主键,缓存永远无法命中,直接访问数据库。

  • 布隆过滤器作为前置过滤器:合法的ID被写入到位数组中,如果过滤器判断为“不存在”,则直接拒绝;错误的判断只会允许通过而不会拦截。
  • 空结果短缓存:当数据库没有数据的时候写入一个空标记,设置TTL为60到300秒之间来防止重复请求。
  • 入口参数检查:非法格式、越界ID等错误信息会被网关拦截。

击穿:守住“刚过期的热点 Key”

当某一个热点Key失效的时候,会有大量的请求去查询数据库,连接池也会全部被占满。

  • 互斥锁重建:使用 SET key value NX EX 来抢占锁,只有单个线程查询数据库并填充数据,其他线程会短暂地重试或者读取旧的数值。
  • 逻辑过期:缓存物理上不会失效,在value中有一个内部的时间戳来标记它是否已经过期了,如果过期的话就会进行异步更新,在主链路上返回的是旧的数据以保证其可用性。
  • 热点预热:大促前主动加载高频数据,避免冷启动瞬时空窗。

雪崩:把“集中失效的大批密钥”打碎

大量的Key已经过期或者Redis无法访问,导致回源流量出现高峰。

  • 随机过期时间:基础TTL加上一个随机的偏移量,例如1800+rand(0,300)来使失效曲线更加平滑。
  • 多级缓存:本地缓存(Caffeine)+ Redis 兜底,减少单点故障。
  • 限流降级:Sentinel 对返回源QPS进行限制,超过阈值则使用默认值来避免数据库被拖垮。

落地片段参考

// 随机 TTL:基础 30 分钟 + 0~300 秒抖动
long ttl = 1800 + ThreadLocalRandom.current().nextLong(0, 300);
redis.set(key, value, ttl, TimeUnit.SECONDS);

// 互斥锁防击穿
String lockKey = "lock:" + key;
if (redis.set(lockKey, "1", "NX", "EX", 3)) {
    try {
        Object data = db.load(key);
        redis.set(key, data, ttl, TimeUnit.SECONDS);
    } finally {
        redis.del(lockKey);
    }
}

主要关注三个方面:缓存命中率、数据库QPS峰值以及慢查询中无效Key所占比例,当命中率低于80%,或者回源QPS翻一番的时候就会发出警报。

总结

穿透防“无”,击穿防“单”,雪崩防“多”。布隆过滤器拦截非法查询,互斥锁收敛热点回源,随机过期解决集中失效——三者合起来并不是增加复杂性,而是把风险从数据库前面转移出去并且分散开来。生产环境根据流量大小分等级进行部署:小系统先填充空值缓存和TTL抖动,大系统再添加布隆过滤器、分布式锁以及多级缓存。

0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
最旧
最新 最多投票