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 缓存失效场景与防护流程
下图展示了从请求入口到数据库的完整链路,以及三种失效场景的拦截点:
2.6 从失效场景到一致性闭环
理解这三种失效场景是构建缓存一致性闭环的前提:
- 穿透和击穿属于局部失效,可以通过布隆过滤器、互斥锁等局部手段解决。
- 雪崩属于系统性失效,需要从架构层面(高可用、限流、降级)进行防御。
- 在后续章节中,Rate Limiting 将作为统一的流量控制手段,与上述方案协同工作,形成“预防 → 拦截 → 限流 → 恢复”的完整闭环。
前置知识:在进入第 3 章“缓存一致性协议”之前,请确保你已经掌握本节内容。缓存失效场景的防护策略,将直接影响一致性协议的设计复杂度。