2. 缓存基础回顾:缓存穿透、缓存击穿、缓存雪崩的定义与常见解决方案

在分布式系统中,缓存层位于数据库与业务逻辑之间,承担着加速读写、保护后端、削峰填谷的核心职责。然而,当缓存层的设计或使用出现偏差时,会引发三种经典故障模式:缓存穿透、缓存击穿与缓存雪崩。理解这三者的本质区别与应对策略,是构建高可用缓存体系的第一步。

2.1 三种故障模式的定义与对比

故障模式 核心定义 触发条件 影响范围 典型特征
缓存穿透 请求查询一个缓存和数据库中都不存在的数据 恶意攻击或非法 key 持续涌入 数据库直接承受所有无效请求 缓存命中率骤降,DB 负载异常升高
缓存击穿 某个热点 key 在缓存失效的瞬间,大量并发请求同时穿透到数据库 热点 key 过期 + 高并发访问 单点数据库压力激增,可能引发级联故障 单个 key 失效后 DB 瞬间 QPS 飙升
缓存雪崩 大量缓存 key 在同一时间集中失效,导致请求全部落到数据库 批量 key 同时过期 / 缓存节点宕机 数据库被海量请求冲垮,系统整体不可用 大面积缓存失效,DB 流量呈洪峰状
关键区分:穿透是“查不到”导致的空回放;击穿是“单个热点”失效导致的并发穿透;雪崩是“大面积”失效导致的洪峰。三者成因不同,解决方案也各有侧重。

2.2 缓存穿透:空回放攻击与防御

缓存穿透的本质是缓存层无法拦截无效请求。当请求的 key 在缓存中不存在,且数据库中也不存在该记录时,每次请求都会直接访问数据库,缓存层形同虚设。

常见解决方案

  • 缓存空对象(Null Object):将数据库查询结果为空的 key 也缓存起来,设置较短的过期时间(如 30~60 秒),避免重复穿透。
  • 布隆过滤器(Bloom Filter):在缓存层之前增加布隆过滤器,快速判断 key 是否可能存在。若过滤器判定不存在,则直接拒绝请求,不再查询缓存或数据库。
  • 参数校验与限流:在网关层对请求参数进行合法性校验,拦截明显非法的 key(如 id < 0、超长字符串等)。
实践建议:布隆过滤器适用于 key 集合相对固定的场景(如用户 ID 范围已知);缓存空对象适用于 key 空间较大但空值比例不高的场景。两者可组合使用。

2.3 缓存击穿:热点 key 失效的并发风暴

缓存击穿只发生在热点 key 上。当某个被高频访问的 key 在缓存中过期,大量并发请求同时发现缓存缺失,全部穿透到数据库,导致数据库瞬间压力过大。

常见解决方案

  • 互斥锁(Mutex Lock):在缓存失效时,只允许一个线程去数据库加载数据,其他线程等待或快速失败。常用实现:Redis SETNX、分布式锁。
  • 逻辑过期(Logical Expiration):缓存数据中额外存储一个逻辑过期时间字段,业务线程发现逻辑过期时,异步更新缓存,同时返回旧数据给调用方,避免阻塞。
  • 热点 key 永不过期:对极端热点的 key 不设置物理过期时间,通过后台定时任务主动更新缓存内容。
// 互斥锁伪代码示例(Java + Redis)
public String getData(String key) {
    String value = redis.get(key);
    if (value != null) {
        return value;
    }
    // 尝试获取分布式锁
    String lockKey = "lock:" + key;
    if (redis.setnx(lockKey, "1", 10, TimeUnit.SECONDS)) {
        try {
            // 从数据库加载数据
            value = db.query(key);
            redis.set(key, value, 300, TimeUnit.SECONDS);
        } finally {
            redis.del(lockKey);
        }
    } else {
        // 未获取到锁,等待后重试或返回降级数据
        Thread.sleep(50);
        return getData(key); // 递归重试
    }
    return value;
}

2.4 缓存雪崩:大面积失效的灾难性后果

缓存雪崩的破坏力最大。当大量缓存 key 在同一时间过期,或缓存节点发生宕机,所有请求瞬间涌向数据库,极易导致数据库连接池耗尽、服务雪崩。

常见解决方案

  • 过期时间随机化:在设置缓存过期时间时,在基础过期时间上增加一个随机偏移量(如 ±10%),避免大量 key 同时过期。
  • 缓存高可用架构:采用 Redis 哨兵模式、集群模式或多级缓存(本地缓存 + 分布式缓存),防止单点故障导致整体失效。
  • 限流与降级:在数据库访问层前置限流组件(如 Sentinel、Hystrix),当流量超过阈值时直接返回降级数据或错误提示。
  • 提前预热:在业务低峰期或系统启动时,主动将热点数据加载到缓存中,避免冷启动导致的雪崩。
注意:缓存雪崩不仅由过期时间引起,缓存节点宕机、网络分区、缓存服务重启等基础设施故障同样会触发雪崩。高可用部署与熔断降级是最后一道防线。

2.5 三种故障模式的关系与综合防御

下图展示了三种故障模式在请求链路中的位置与防御层次:

缓存故障模式防御层次图 客户端请求 布隆过滤器 缓存层(Redis) 数据库 穿透防御 击穿 / 雪崩防御 缓存穿透防御 • 布隆过滤器拦截不存在 key • 缓存空对象 + 短过期时间 • 网关层参数校验与限流 缓存击穿防御 • 互斥锁(分布式锁) • 逻辑过期 + 异步更新 • 热点 key 永不过期 + 后台刷新 缓存雪崩防御 • 过期时间随机化(基础时间 ± 偏移) • 缓存高可用(哨兵 / 集群 / 多级缓存) • 限流降级 + 提前预热 综合防御体系 • 穿透 + 击穿 + 雪崩 分层治理 • 监控告警:缓存命中率、DB 慢查询 • 定期压测与故障演练

2.6 解决方案选型速查表

场景 首选方案 备选方案 适用条件
缓存穿透 布隆过滤器 缓存空对象 key 集合有限且可预知
缓存击穿 互斥锁 逻辑过期 热点 key 数量少、并发极高
缓存雪崩 过期时间随机化 多级缓存 + 限流 批量 key 过期场景
混合场景 分层防御组合 全链路监控 + 自动降级 高可用要求严格的系统
核心原则:缓存故障防御不是单一技术的堆砌,而是根据业务流量特征、数据一致性要求、运维成本综合权衡的结果。穿透靠过滤、击穿靠锁、雪崩靠分散,三者缺一不可。