3. 缓存穿透与雪崩:问题定义、常见解决方案、版本校验在其中的角色
缓存穿透与缓存雪崩是分布式缓存系统中两类最典型的高可用风险。它们虽然表现不同,但都会导致后端数据库承受远超预期的负载,甚至引发级联故障。本章从问题定义出发,梳理业界主流防御手段,并重点分析版本校验机制在这两类场景中的实际作用与局限性。
3.1 缓存穿透
3.1.1 问题定义
缓存穿透指查询一个根本不存在的数据。由于缓存中不存在该 key,请求直接穿透到数据库;数据库也查不到结果,不会回写缓存。大量此类请求(尤其是恶意攻击)会直接压垮数据库。
| 维度 | 说明 |
|---|---|
| 触发条件 | 查询的 key 在缓存和数据库中均不存在 |
| 典型场景 | 恶意攻击(遍历不存在的 ID)、业务 bug 产生无效 key |
| 影响范围 | 数据库连接耗尽、响应超时、服务雪崩 |
3.1.2 常见解决方案
- 缓存空对象:将 null 结果也缓存,设置较短 TTL(如 30~60 秒),防止同一无效 key 反复穿透。
- 布隆过滤器(Bloom Filter):在缓存前置一个位图结构,快速判断 key 是否存在。不存在则直接拒绝,避免查询数据库。
- 参数校验与限流:对请求参数做合法性校验(如 ID 范围、格式),配合接口限流降低穿透流量。
实践建议:布隆过滤器适合“key 集合相对固定”的场景(如用户 ID 范围已知);缓存空对象更灵活,但需要防范空对象堆积占用内存。
3.1.3 版本校验在穿透中的角色
版本校验不能直接防御缓存穿透。因为穿透的核心问题是“key 不存在”,而版本校验的前提是“key 存在且需要比对版本号”。不过,版本校验可以间接减少穿透后的影响:
- 当缓存空对象携带版本号时,客户端可以识别“空版本”并提前终止重试。
- 在分布式场景中,版本校验配合布隆过滤器,可以避免因版本不一致导致的无效回源。
3.2 缓存雪崩
3.2.1 问题定义
缓存雪崩指大量缓存 key 在同一时间失效,或缓存节点整体宕机,导致所有请求直接打到数据库。与穿透不同,雪崩的 key 在数据库中真实存在,但缓存层瞬间失效,流量洪峰远超数据库处理能力。
| 维度 | 说明 |
|---|---|
| 触发条件 | 大量 key 同时过期 / 缓存节点宕机 |
| 典型场景 | 热点数据批量过期、缓存集群重启、网络分区 |
| 影响范围 | 数据库瞬时 QPS 飙升、慢查询堆积、服务不可用 |
3.2.2 常见解决方案
- 过期时间加随机偏移:在基础 TTL 上增加 ±10%~30% 的随机值,避免大量 key 同时过期。
- 缓存预热 + 多级缓存:提前加载热点数据,本地缓存(如 Caffeine)作为二级缓存兜底。
- 限流降级与熔断:对数据库访问做线程池隔离、限流,超出阈值直接返回降级结果。
- 高可用缓存集群:使用 Redis Sentinel 或 Cluster 模式,避免单点故障。
注意:雪崩的破坏力远大于穿透。穿透通常只影响不存在的 key,而雪崩会导致大量正常业务 key 同时失效,数据库压力呈指数级增长。
3.2.3 版本校验在雪崩中的角色
版本校验在雪崩场景中扮演关键辅助角色:
- 版本号作为“逻辑过期时间”:即使缓存 key 的物理 TTL 已过期,只要版本号与数据库一致,仍可继续使用缓存,避免回源。
- 版本协商减少回源量:当大量 key 同时过期时,客户端通过版本号判断缓存是否仍有效,只有版本不一致时才回源数据库。
- 配合分布式锁:在缓存重建时,版本校验可以防止多个线程同时回源,降低数据库压力。
3.3 版本校验在穿透与雪崩中的定位总结
| 场景 | 版本校验是否直接防御 | 实际作用 | 推荐配合方案 |
|---|---|---|---|
| 缓存穿透 | 否 | 间接辅助:空对象版本化、避免无效重试 | 布隆过滤器 + 缓存空对象 |
| 缓存雪崩 | 是(辅助) | 逻辑过期、版本协商减少回源、防并发重建 | TTL 随机偏移 + 多级缓存 + 限流 |
核心结论:版本校验不是穿透和雪崩的首选防御手段,但它是缓存一致性保障体系的重要组成部分。在雪崩场景中,版本校验能显著降低回源频率;在穿透场景中,它更多作为空对象缓存的补充机制。实际生产环境中,应将版本校验与布隆过滤器、TTL 随机化、限流降级等方案组合使用,形成多层防御体系。