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 三种故障模式的关系与综合防御
下图展示了三种故障模式在请求链路中的位置与防御层次:
2.6 解决方案选型速查表
| 场景 | 首选方案 | 备选方案 | 适用条件 |
|---|---|---|---|
| 缓存穿透 | 布隆过滤器 | 缓存空对象 | key 集合有限且可预知 |
| 缓存击穿 | 互斥锁 | 逻辑过期 | 热点 key 数量少、并发极高 |
| 缓存雪崩 | 过期时间随机化 | 多级缓存 + 限流 | 批量 key 过期场景 |
| 混合场景 | 分层防御组合 | 全链路监控 + 自动降级 | 高可用要求严格的系统 |
核心原则:缓存故障防御不是单一技术的堆砌,而是根据业务流量特征、数据一致性要求、运维成本综合权衡的结果。穿透靠过滤、击穿靠锁、雪崩靠分散,三者缺一不可。