3. 缓存穿透与雪崩:问题定义、常见解决方案、版本校验在其中的角色

缓存穿透与缓存雪崩是分布式缓存系统中两类最典型的高可用风险。它们虽然表现不同,但都会导致后端数据库承受远超预期的负载,甚至引发级联故障。本章从问题定义出发,梳理业界主流防御手段,并重点分析版本校验机制在这两类场景中的实际作用与局限性。

3.1 缓存穿透

3.1.1 问题定义

缓存穿透指查询一个根本不存在的数据。由于缓存中不存在该 key,请求直接穿透到数据库;数据库也查不到结果,不会回写缓存。大量此类请求(尤其是恶意攻击)会直接压垮数据库。

维度 说明
触发条件 查询的 key 在缓存和数据库中均不存在
典型场景 恶意攻击(遍历不存在的 ID)、业务 bug 产生无效 key
影响范围 数据库连接耗尽、响应超时、服务雪崩

3.1.2 常见解决方案

  1. 缓存空对象:将 null 结果也缓存,设置较短 TTL(如 30~60 秒),防止同一无效 key 反复穿透。
  2. 布隆过滤器(Bloom Filter):在缓存前置一个位图结构,快速判断 key 是否存在。不存在则直接拒绝,避免查询数据库。
  3. 参数校验与限流:对请求参数做合法性校验(如 ID 范围、格式),配合接口限流降低穿透流量。
实践建议:布隆过滤器适合“key 集合相对固定”的场景(如用户 ID 范围已知);缓存空对象更灵活,但需要防范空对象堆积占用内存。

3.1.3 版本校验在穿透中的角色

版本校验不能直接防御缓存穿透。因为穿透的核心问题是“key 不存在”,而版本校验的前提是“key 存在且需要比对版本号”。不过,版本校验可以间接减少穿透后的影响:

  • 当缓存空对象携带版本号时,客户端可以识别“空版本”并提前终止重试。
  • 在分布式场景中,版本校验配合布隆过滤器,可以避免因版本不一致导致的无效回源。
缓存穿透流程与防御对比 客户端请求 缓存未命中 数据库查询 返回空/崩溃 布隆过滤器 直接拒绝 缓存空对象 缓存 null + 版本 版本校验(间接辅助)

3.2 缓存雪崩

3.2.1 问题定义

缓存雪崩指大量缓存 key 在同一时间失效,或缓存节点整体宕机,导致所有请求直接打到数据库。与穿透不同,雪崩的 key 在数据库中真实存在,但缓存层瞬间失效,流量洪峰远超数据库处理能力。

维度 说明
触发条件 大量 key 同时过期 / 缓存节点宕机
典型场景 热点数据批量过期、缓存集群重启、网络分区
影响范围 数据库瞬时 QPS 飙升、慢查询堆积、服务不可用

3.2.2 常见解决方案

  1. 过期时间加随机偏移:在基础 TTL 上增加 ±10%~30% 的随机值,避免大量 key 同时过期。
  2. 缓存预热 + 多级缓存:提前加载热点数据,本地缓存(如 Caffeine)作为二级缓存兜底。
  3. 限流降级与熔断:对数据库访问做线程池隔离、限流,超出阈值直接返回降级结果。
  4. 高可用缓存集群:使用 Redis Sentinel 或 Cluster 模式,避免单点故障。
注意:雪崩的破坏力远大于穿透。穿透通常只影响不存在的 key,而雪崩会导致大量正常业务 key 同时失效,数据库压力呈指数级增长。

3.2.3 版本校验在雪崩中的角色

版本校验在雪崩场景中扮演关键辅助角色:

  • 版本号作为“逻辑过期时间”:即使缓存 key 的物理 TTL 已过期,只要版本号与数据库一致,仍可继续使用缓存,避免回源。
  • 版本协商减少回源量:当大量 key 同时过期时,客户端通过版本号判断缓存是否仍有效,只有版本不一致时才回源数据库。
  • 配合分布式锁:在缓存重建时,版本校验可以防止多个线程同时回源,降低数据库压力。
缓存雪崩与版本校验的防御机制 大量 key 同时过期 请求穿透到 DB 数据库过载崩溃 TTL 随机偏移 多级缓存兜底 限流熔断 版本校验 逻辑过期 + 版本协商 减少回源量 保护数据库

3.3 版本校验在穿透与雪崩中的定位总结

场景 版本校验是否直接防御 实际作用 推荐配合方案
缓存穿透 否 间接辅助:空对象版本化、避免无效重试 布隆过滤器 + 缓存空对象
缓存雪崩 是(辅助) 逻辑过期、版本协商减少回源、防并发重建 TTL 随机偏移 + 多级缓存 + 限流
核心结论:版本校验不是穿透和雪崩的首选防御手段,但它是缓存一致性保障体系的重要组成部分。在雪崩场景中,版本校验能显著降低回源频率;在穿透场景中,它更多作为空对象缓存的补充机制。实际生产环境中,应将版本校验与布隆过滤器、TTL 随机化、限流降级等方案组合使用,形成多层防御体系。