Redis 缓存穿透、击穿与雪崩:原理与解决方案

Redis 缓存穿透、击穿与雪崩:原理与解决方案

用 Redis 做缓存几乎是后端项目的标配,但缓存用不好,反而会在高并发时把数据库拖垮。缓存穿透、击穿、雪崩是最常被问到、也最容易踩坑的三个问题,这篇文章把它们捋清楚。

1. 缓存穿透:查询一个根本不存在的数据

现象:请求查询一个数据库里也不存在的 key,缓存里自然也没有,每次请求都要打到数据库上。如果有人恶意用不存在的 id 疯狂请求,数据库很容易被打垮。

解决方案

  • 缓存空值:查询结果为空时,也把这个空结果缓存起来(设置一个较短的过期时间),避免同一个不存在的 key 反复穿透到数据库。
value = redis_client.get(key)
if value is None:
    value = query_from_db(key)
    if value is None:
        redis_client.setex(key, 60, '')  # 缓存空值,60 秒过期
    else:
        redis_client.setex(key, 3600, value)
  • 布隆过滤器:提前把所有合法的 key 放进布隆过滤器,请求先经过过滤器判断,不在里面的 key 直接拒绝,连缓存都不用查。

2. 缓存击穿:一个热点 key 突然过期

现象:某个访问量极高的 key(比如首页热门文章)缓存过期的瞬间,大量并发请求同时穿透到数据库,短时间内造成数据库压力骤增。

解决方案

  • 加互斥锁:缓存失效时只让一个请求去查数据库并重建缓存,其他请求等待或读旧值。
lock_key = f'lock:{key}'
if redis_client.set(lock_key, '1', nx=True, ex=10):
    try:
        value = query_from_db(key)
        redis_client.setex(key, 3600, value)
    finally:
        redis_client.delete(lock_key)
else:
    time.sleep(0.05)
    value = redis_client.get(key)  # 重试读取
  • 热点数据不过期:对特别热门的 key,直接不设置过期时间,靠后台任务异步更新。

3. 缓存雪崩:大量 key 集中在同一时刻过期

现象:如果给一批 key 设置了相同的过期时间,到了那个时间点,缓存集体失效,请求瞬间全部涌向数据库。

解决方案

  • 过期时间加随机值:避免大量 key 在同一时刻集中失效。
import random

expire_time = 3600 + random.randint(0, 300)  # 基础 1 小时 + 0~5 分钟随机抖动
redis_client.setex(key, expire_time, value)
  • 多级缓存:本地缓存(如内存缓存)+ Redis 缓存,即使 Redis 整体不可用,也能兜底一部分请求。
  • 限流 + 降级:数据库压力过大时,通过限流保护数据库,必要时返回降级数据(比如旧数据或默认值)。

小结

问题 触发场景 核心思路
缓存穿透 查询不存在的数据 缓存空值 / 布隆过滤器
缓存击穿 单个热点 key 过期 互斥锁 / 热点不过期
缓存雪崩 大量 key 同时过期 过期时间加随机 / 多级缓存

这三个问题看着相似,但触发条件和解法都不一样,面试也经常放在一起问。理解清楚各自的场景,才能在实际项目里对症下药。

作者: yanleaf 发布时间: 2026-07-03 15:58:23 阅读数: 187 点赞数: 0 评论数:

评论区

备案期间,评论功能暂时关闭。