2. 缓存一致性基础:缓存穿透、缓存击穿、缓存雪崩的定义与经典解决方案

在构建高并发缓存系统时,缓存与数据库之间的数据一致性是核心挑战。然而,在讨论一致性协议之前,必须先解决三个经典的缓存失效场景——缓存穿透、缓存击穿和缓存雪崩。这三者虽然名称相似,但成因、影响范围和解决方案截然不同。本节将逐一拆解其定义、发生机理及业界经典应对策略,为后续的 Rate Limiting 闭环控制奠定基础。

2.1 缓存穿透(Cache Penetration)

定义:查询一个根本不存在的数据(既不在缓存中,也不在数据库中),导致每次请求都直接穿透缓存层,打到数据库层。由于数据库也返回空结果,缓存层无法写入有效数据,后续相同请求会持续穿透。

核心特征:缓存和数据库都查不到,请求绕过缓存直达数据库。

2.1.1 经典解决方案

方案 原理 优点 缺点
缓存空对象 将数据库返回的 null 也缓存起来,设置较短的 TTL(如 30~60 秒) 实现简单,直接阻断穿透 占用缓存空间;短时间大量空 key 可能引发缓存雪崩
布隆过滤器(Bloom Filter) 在缓存层前置一个位图结构,快速判断 key 是否存在 空间效率极高,不存在则直接拒绝 存在误判率;不支持删除操作;需要定期重建
参数校验与限流 对请求参数做合法性校验,非法 ID 直接返回 从入口拦截无效请求 无法拦截合法但不存在的数据
工程建议:生产环境通常组合使用“布隆过滤器 + 缓存空对象”。布隆过滤器拦截大部分不存在 key,少量误判的 key 通过缓存空对象兜底,避免数据库被打穿。

2.2 缓存击穿(Cache Breakdown)

定义:一个热点 key 在缓存过期失效的瞬间,大量并发请求同时涌入,全部穿透到数据库,导致数据库瞬间负载飙升甚至崩溃。与穿透不同,击穿针对的是存在但已过期的热点数据。

核心特征:热点 key 过期瞬间,高并发集中打向数据库。

2.2.1 经典解决方案

方案 原理 适用场景
互斥锁(Mutex Lock) 缓存失效后,只允许一个线程去数据库加载数据,其他线程等待或重试 写少读多、并发量可控的场景
逻辑过期(预更新) 缓存中存储数据的同时存储过期时间,后台异步线程提前刷新 对一致性要求不苛刻、可容忍短暂脏读
永不过期 + 异步更新 缓存 key 不设置物理过期时间,由后台任务定期更新 数据变化频率稳定、可接受延迟
// 互斥锁伪代码示例(Redis + SETNX)
public String getData(String key) {
    String value = redis.get(key);
    if (value != null) {
        return value;
    }
    // 尝试获取分布式锁
    if (redis.setnx("lock:" + key, "1", 3)) {
        try {
            value = db.query(key);
            redis.set(key, value, 300);
            return value;
        } finally {
            redis.del("lock:" + key);
        }
    } else {
        // 未获取到锁,等待后重试
        Thread.sleep(50);
        return getData(key);
    }
}
避坑提示:互斥锁方案在高并发下可能导致大量线程阻塞等待,增加响应延迟。建议结合“缓存预热”和“降级策略”使用,避免锁成为新的瓶颈。

2.3 缓存雪崩(Cache Avalanche)

定义:大量缓存 key 在同一时间过期失效,或缓存节点宕机,导致海量请求直接涌入数据库,造成数据库压力激增甚至宕机。雪崩是穿透和击穿的放大版,影响范围覆盖整个缓存层。

核心特征:大规模 key 同时失效,或缓存集群整体不可用。

2.3.1 经典解决方案

方案 原理 效果
过期时间随机化 在基础 TTL 上增加随机偏移(如 ±20%) 避免大量 key 同时过期
缓存高可用(Redis Cluster/Sentinel) 部署主从、哨兵或集群模式,防止单点故障 提升缓存层可用性
多级缓存(本地 + 分布式) 本地缓存(如 Caffeine)作为第一级,分布式缓存作为第二级 分散数据库压力
限流降级(熔断器) 对数据库访问进行限流,超出阈值直接返回降级数据 保护数据库不被击穿

2.4 三种场景对比总结

场景 触发条件 影响范围 核心解决思路
缓存穿透 查询不存在的数据 单个 key 持续穿透 布隆过滤器 + 空对象缓存
缓存击穿 热点 key 过期瞬间 单个热点 key 互斥锁 / 逻辑过期
缓存雪崩 大量 key 同时过期 / 缓存宕机 整个缓存层 过期随机化 + 高可用 + 限流降级

2.5 缓存失效场景与防护流程

下图展示了从请求入口到数据库的完整链路,以及三种失效场景的拦截点:

缓存穿透、击穿、雪崩防护流程 缓存失效场景与防护流程 客户端请求 布隆过滤器(穿透拦截) 直接返回空 不存在 存在 缓存层(Redis Cluster) 返回缓存数据 命中 未命中 互斥锁(击穿防护) 数据库(DB) 限流降级(雪崩防护) 说明 穿透:布隆过滤器拦截不存在 key | 击穿:互斥锁保护热点 key 过期瞬间 | 雪崩:限流降级 + 过期随机化 + 缓存高可用

2.6 从失效场景到一致性闭环

理解这三种失效场景是构建缓存一致性闭环的前提:

  • 穿透和击穿属于局部失效,可以通过布隆过滤器、互斥锁等局部手段解决。
  • 雪崩属于系统性失效,需要从架构层面(高可用、限流、降级)进行防御。
  • 在后续章节中,Rate Limiting 将作为统一的流量控制手段,与上述方案协同工作,形成“预防 → 拦截 → 限流 → 恢复”的完整闭环。
前置知识:在进入第 3 章“缓存一致性协议”之前,请确保你已经掌握本节内容。缓存失效场景的防护策略,将直接影响一致性协议的设计复杂度。