4. 缓存一致性基础:缓存穿透、缓存击穿、缓存雪崩的定义与典型场景

在分布式限流熔断治理中,缓存层是抵御突发流量、保护后端数据库的第一道防线。然而,缓存层自身的故障模式——缓存穿透、缓存击穿、缓存雪崩——会直接导致请求绕过缓存直击数据库,引发级联故障。理解这三种故障的定义、成因与典型场景,是定位缓存一致性故障的前提。

4.1 三种故障的核心定义与对比

故障类型 定义 核心成因 影响范围
缓存穿透 请求查询一个一定不存在的数据,缓存和数据库都查不到,每次请求都穿透到数据库。 恶意攻击或业务漏洞(如查询不存在的用户ID) 单点持续攻击,数据库压力线性增长
缓存击穿 一个热点 key 在缓存失效的瞬间,大量并发请求同时穿透到数据库。 热点 key 过期 + 高并发 单 key 瞬时击穿,数据库单点压力暴增
缓存雪崩 大量缓存 key 在同一时间失效,或缓存节点宕机,导致所有请求涌向数据库。 批量 key 同时过期 / 缓存集群故障 全局性故障,数据库可能直接被压垮
治理视角提示:在 OpenAPI 限流熔断治理中,缓存穿透通常由参数校验层拦截;缓存击穿需要热点 key 自动续期或互斥锁;缓存雪崩则依赖缓存高可用架构与过期时间随机化。三种故障的治理策略不同,但都会引发缓存一致性告警。

4.2 缓存穿透:空查询的陷阱

典型场景:攻击者构造大量不存在的用户 ID(如负数、超长数字)请求用户信息接口。缓存中无此 key,数据库中也无此记录,每次请求都直接查询数据库。

  • 危害:数据库连接数被无效查询耗尽,正常请求被阻塞。
  • 缓存一致性表现:缓存命中率骤降,数据库 QPS 飙升,限流器频繁触发。
  • 常见治理手段:缓存空值(设置短 TTL)、布隆过滤器前置拦截。
缓存穿透流程示意图 恶意客户端 缓存层 数据库 请求不存在key 缓存未命中 查询数据库(无结果) 返回空(不缓存) 后果:每次无效请求都穿透到数据库,缓存形同虚设 治理:缓存空值 + 布隆过滤器

4.3 缓存击穿:热点 key 的瞬间崩塌

典型场景:某电商平台的爆款商品详情页,缓存 key 为 product:12345。该 key 在缓存中的 TTL 恰好到期,此时数千个并发请求同时读取该 key,全部未命中缓存,直接查询数据库。

  • 危害:数据库单行记录被高频查询,行锁竞争激烈,响应时间飙升。
  • 缓存一致性表现:缓存命中率在某一秒内断崖式下跌,数据库 QPS 出现尖峰。
  • 常见治理手段:互斥锁(只允许一个线程重建缓存)、热点 key 永不过期 + 异步更新。
限流熔断关联:缓存击穿发生时,限流器会检测到数据库 QPS 突增,可能触发熔断。但熔断后所有请求被拒绝,反而加剧用户体验恶化。正确的做法是优先使用互斥锁保护热点 key,而非依赖限流器兜底。

4.4 缓存雪崩:全局性的灾难

典型场景:某系统所有缓存 key 的过期时间统一设置为 1 小时。在整点时刻,大量 key 同时过期,所有请求同时穿透到数据库。或者,缓存集群中某台机器宕机,该机器上的所有 key 全部失效。

  • 危害:数据库瞬间承受数倍于平时的流量,极易触发连接池耗尽、CPU 满载,导致数据库宕机。
  • 缓存一致性表现:缓存命中率全局归零,数据库 QPS 达到峰值,系统整体响应时间急剧恶化。
  • 常见治理手段:过期时间随机化(基础 TTL + 随机偏移)、缓存集群高可用(主从 + 哨兵)、本地缓存二级降级。
缓存雪崩:批量 key 同时过期 T0 T0 + 1h(过期时刻) T0 + 2h key_1 (有效) key_2 (有效) key_3 (有效) key_1 (失效) key_2 (失效) key_3 (失效) 数据库:瞬间承受全部请求 治理:过期时间随机化 + 缓存高可用 + 本地缓存降级

4.5 三种故障在限流熔断治理中的表现差异

故障类型 限流器触发特征 熔断器触发特征 缓存一致性告警特征
缓存穿透 持续低水平限流(无效请求不断) 不易触发熔断(QPS 未达阈值) 缓存命中率持续低位,数据库 QPS 缓慢上升
缓存击穿 瞬时尖峰限流(1秒内 QPS 暴增) 可能触发熔断(错误率飙升) 缓存命中率断崖式下跌,数据库 QPS 尖峰
缓存雪崩 全局性限流(所有接口 QPS 暴涨) 大概率触发熔断(全局超时) 缓存命中率全局归零,数据库 QPS 达到峰值
故障定位陷阱:当限流器频繁触发时,不要只关注限流阈值配置。先检查缓存命中率指标——如果命中率正常,可能是流量突增;如果命中率异常,则优先排查缓存穿透/击穿/雪崩。这是区分“真实流量过载”与“缓存故障导致过载”的关键。

4.6 典型故障排查步骤

  1. 查看缓存命中率曲线:如果命中率从 95% 骤降至 10% 以下,优先怀疑缓存雪崩或击穿。
  2. 检查 key 过期分布:使用 redis-cli --bigkeys 或类似工具,查看是否有大量 key 在同一时间过期。
  3. 分析请求日志:筛选出缓存未命中的请求,查看请求的 key 是否集中在一个热点 key(击穿)还是分散在大量不存在的 key(穿透)。
  4. 验证数据库 QPS 与缓存 QPS 的比例:正常情况下数据库 QPS 应远小于缓存 QPS;如果比例接近 1:1,说明缓存基本失效。
  5. 检查限流器日志:限流触发的时间点是否与缓存命中率下跌的时间点吻合。
# 示例:使用 Redis 命令检查 key 过期时间分布
redis-cli --scan --pattern "product:*" | head -100 | while read key; do
  ttl=$(redis-cli ttl "$key")
  echo "$key TTL: $ttl"
done
# 如果大量 key 的 TTL 集中在同一秒,说明存在雪崩风险
最佳实践:在 OpenAPI 网关层统一记录缓存命中/未命中日志,并关联限流事件 ID。这样在故障定位时,可以快速关联“限流触发 → 缓存未命中 → 数据库压力”的完整链路。