2. 缓存一致性基础:缓存穿透、缓存击穿、缓存雪崩的定义与典型场景
在分布式系统中,缓存是提升读取性能、保护后端数据库的关键手段。然而,缓存与数据库之间的数据不一致、缓存失效的并发冲击,会引发三类经典故障模式:缓存穿透、缓存击穿和缓存雪崩。理解这三者的定义、成因与典型场景,是设计缓存一致性治理与错误处理链路的前提。
2.1 三类故障的定义与核心差异
| 故障类型 | 定义 | 核心成因 | 影响范围 |
|---|---|---|---|
| 缓存穿透 | 请求查询一个缓存和数据库中都不存在的数据,导致每次请求都穿透缓存直达数据库。 | 恶意攻击或业务漏洞,查询不存在的主键/索引。 | 数据库压力陡增,可能拖垮 DB。 |
| 缓存击穿 | 一个热点 key 在缓存失效的瞬间,大量并发请求同时回源数据库,导致数据库负载瞬间飙升。 | 热点 key 过期 + 高并发访问。 | 单点数据库压力激增,可能引发局部故障。 |
| 缓存雪崩 | 大量缓存 key 在同一时间或短时间内集中失效,导致海量请求直接打到数据库,引发系统级崩溃。 | 缓存服务宕机、统一过期时间设置不当。 | 全系统数据库过载,可能引发级联故障。 |
关键区分:穿透是“查不到”导致绕过缓存;击穿是“单个热点”失效导致并发回源;雪崩是“大量 key”同时失效导致整体冲击。
2.2 缓存穿透:定义与典型场景
定义:客户端请求一个缓存和数据库中都不存在的 key,缓存无法命中,每次请求都直接查询数据库。由于数据库中也不存在该记录,查询结果为空,缓存不会回写,导致后续相同请求持续穿透。
典型场景:
- 恶意攻击:攻击者利用系统漏洞,构造大量不存在的 ID(如负数 ID、超长 ID、随机字符串)发起请求,绕过缓存直接攻击数据库。
- 业务数据删除:某条数据被从数据库中物理删除,但缓存中对应的 key 未被清理,后续请求查缓存未命中,查数据库也返回空,形成穿透。
- 参数校验缺失:前端或上游服务未对请求参数做合法性校验,导致非法参数(如不存在的用户 ID)直接穿透到后端。
治理思路:布隆过滤器(Bloom Filter)前置拦截、缓存空对象(Null Object)并设置短过期时间、参数合法性校验。
2.3 缓存击穿:定义与典型场景
定义:一个被高频访问的热点 key 在缓存过期的瞬间,大量并发请求同时发现缓存失效,于是全部回源数据库查询,导致数据库在短时间内承受远超正常水平的读压力。
典型场景:
- 秒杀活动商品:某爆款商品的详情 key 在秒杀开始瞬间过期,成千上万的用户同时刷新页面,请求全部穿透到数据库。
- 热门新闻/微博:一条突发新闻的阅读量 key 在缓存失效后,大量用户同时访问,导致数据库查询量瞬间暴涨。
- 全局配置项:系统全局配置的缓存 key 过期后,所有服务实例同时回源拉取配置,造成配置中心或数据库压力飙升。
注意:缓存击穿与穿透不同,击穿的目标 key 在数据库中是真实存在的,只是缓存恰好失效。治理重点在于“互斥回源”和“热点永不过期”。
2.4 缓存雪崩:定义与典型场景
定义:大量缓存 key 在同一时间窗口内集中失效,或者缓存服务整体不可用(如 Redis 集群宕机),导致所有请求直接落到数据库,引发数据库过载、连接池耗尽,甚至系统整体不可用。
典型场景:
- 统一过期时间:所有缓存 key 设置了相同的过期时间(如凌晨 0 点),导致在 0 点整大量 key 同时失效,引发雪崩。
- 缓存服务宕机:Redis 集群因网络分区、内存溢出或主从切换失败而整体不可用,所有请求直接回源数据库。
- 缓存预热失败:系统重启后,缓存中无数据,大量请求同时涌入数据库进行预热,导致数据库瞬间被打满。
治理思路:过期时间加随机偏移、缓存高可用(Redis Sentinel/Cluster)、本地缓存(L1 Cache)兜底、限流熔断降级。
2.5 三类故障的关系与对比
2.6 典型故障链路示例
以下代码模拟了一个未做任何防护的缓存查询链路,该链路在面对穿透、击穿、雪崩时均会暴露问题:
// 未防护的缓存查询示例(Java 伪代码)
public User getUserById(String userId) {
// 1. 查缓存
User user = redis.get("user:" + userId);
if (user != null) {
return user;
}
// 2. 缓存未命中,查数据库
user = userDao.selectById(userId);
if (user != null) {
// 3. 回写缓存
redis.set("user:" + userId, user, 3600);
}
// 4. 返回结果(若 user 为 null,则穿透)
return user;
}
问题分析:上述代码未处理“查不到”时的空对象缓存,未对热点 key 做互斥回源,未对过期时间做随机化。在并发场景下,该代码会同时引发穿透、击穿和雪崩风险。
2.7 本章小结
- 缓存穿透:查不到数据,每次穿透到 DB。治理核心:前置过滤 + 空对象缓存。
- 缓存击穿:热点 key 失效瞬间,高并发回源 DB。治理核心:互斥锁 + 热点永不过期。
- 缓存雪崩:大量 key 同时失效或缓存宕机,整体冲击 DB。治理核心:过期时间随机化 + 缓存高可用 + 限流降级。
- 三类故障并非孤立,穿透可能诱发击穿,击穿积累可能演变为雪崩。在 OpenAPI 契约设计中,错误响应体必须明确区分“数据不存在”“缓存失效”“系统过载”等不同错误码,以便客户端采取差异化重试策略。