首页/数据/NoSQL 速查/缓存穿透、击穿与雪崩的处置
选型与架构50 条

缓存穿透、击穿与雪崩的处置

三类问题根因不同:穿透是不存在的 key 反复回源;击穿是热点 key 过期瞬间并发重建;雪崩是大批 key 同时失效或缓存层故障。对应处置是空值缓存/布隆过滤器、互斥重建或逻辑过期、TTL 抖动与降级预案。

NoSQL 速查数据更新于 2026-10-13

缓存三类问题是什么

缓存的三类问题根因不同:穿透是不存在的 key 反复回源;击穿是单个热点 key 过期瞬间并发重建;雪崩是大批 key 同时失效或缓存层整体不可用。对应处置分别是空值缓存与布隆过滤器、互斥重建或逻辑过期、TTL 抖动与降级预案。判断问题类型时先看回源请求的特征:是「缓存与库都没有」还是「缓存没命中但库里有」,两者的处置手段完全不同。

基本信息

穿透查询不存在的 key,缓存与库都没有
击穿单个热点 key 过期瞬间并发回源重建
雪崩大批 key 同时失效或缓存层整体故障
常用手段空值缓存、布隆过滤器、互斥重建、逻辑过期、TTL 抖动、限流降级
所属系统缓存架构通用问题

问题对照

问题触发场景常用处置
穿透查不存在的 key,缓存与库都没有缓存空值(短 TTL)、参数校验、布隆过滤器
击穿单个热点 key 过期,并发同时回源互斥重建、逻辑过期后台刷新、预热
雪崩大批 key 同时失效或缓存整体不可用TTL 加随机抖动、多级缓存、限流降级

典型使用场景

  • 接口加固:对明显非法的查询参数先做校验,减少无效回源
  • 热点预热:活动开始前把热点数据提前写入缓存
  • 空值兜底:对确认不存在的 key 缓存一个短 TTL 的空标记
  • 降级预案:缓存不可用时返回旧数据或限流,避免压力全落到数据库
  • 监控告警:把缓存命中率与回源 QPS 纳入监控

互斥重建(伪代码)

互斥重建(伪代码)
def get_article(aid):
    v = cache.get(aid)                        # 先读缓存
    if v:
        return v
    if cache.setnx("lock:" + aid, "1", 10):  # 抢到锁的请求去回源
        try:
            v = db.load(aid)
            cache.set(aid, v, ttl=600)        # 回填并设置过期时间
        finally:
            cache.delete("lock:" + aid)       # 释放锁
    else:
        sleep(0.05)                           # 未抢到锁的短暂等待,生产要限次退避
    return cache.get(aid)
逻辑过期:value 里带 expire_at 字段而物理上不设过期,逻辑过期后先返回旧值同时异步刷新,适合能容忍短暂旧数据的热点页。
布隆过滤器存在误判,可能把不存在的 key 判为存在,只能作为一层过滤,不能当唯一防线。
空值缓存要能区分真实的空结果与异常导致的空结果,并设置较短 TTL 控制内存占用。

易错点

  • 布隆过滤器存在误判,可能把不存在的 key 判为存在,不能当唯一防线
  • 空值缓存要能区分真实空结果与异常空结果,并设置较短 TTL 控制内存占用
  • 缓存与数据库的写入顺序不做定义,更新期间会出现长时间脏数据
  • 没有降级预案:缓存层故障时全部请求直接压到数据库,故障范围被放大

数据来源与参考

  • Redis Docs: Key eviction
  • Hazelcast Docs: Cache stampede(概念参考)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌