2. 缓存基础回顾:缓存穿透、缓存击穿、缓存雪崩
在分布式系统与 Kubernetes 环境下,缓存是保障读性能的核心手段。然而,缓存层在极端流量下会暴露出三类经典故障模式:缓存穿透、缓存击穿与缓存雪崩。理解这三者的成因与解决方案,是后续进行缓存一致性治理与索引重构的前提。
2.1 缓存穿透
定义:请求查询一个缓存中不存在、数据库中也不存在的数据。由于缓存未命中,每次请求都直接穿透到数据库,导致数据库压力陡增,甚至引发故障。
成因分析
- 恶意攻击:攻击者构造大量不存在的 key(如负 ID、超长字符串)发起请求。
- 业务漏洞:查询参数未做合法性校验,导致无效查询。
- 数据删除:缓存与数据库数据不一致,缓存未及时清理。
经典解决方案
| 方案 | 原理 | 适用场景 | 注意事项 |
|---|---|---|---|
| 布隆过滤器 | 将所有可能存在的数据哈希到一个足够大的 bitmap 中,不存在的数据直接拦截 | 数据量固定、误判率可接受 | 存在误判率;不支持删除元素 |
| 缓存空对象 | 对查询结果为 null 的 key 也缓存,设置较短过期时间(如 30s) | 空 key 数量可控 | 可能缓存大量空 key,占用内存 |
| 参数校验 | 在 API 网关或业务层对参数进行合法性校验 | 所有场景 | 只能防御格式异常,无法防御业务上不存在的 key |
实践建议:在 Kubernetes 环境下,布隆过滤器可以以 Sidecar 模式部署在业务 Pod 中,或通过 Redis 的 BF 模块实现。对于空对象缓存,建议使用
NX + EX 原子操作,避免并发写入。
2.2 缓存击穿
定义:一个热点 key 在缓存过期的一瞬间,大量并发请求同时回源查询数据库,导致数据库压力瞬间飙升。
成因分析
- 热点 key 过期:某个被高频访问的 key 到达 TTL 时间被删除。
- 并发回源:在 key 失效的瞬间,所有请求都未命中缓存,同时穿透到数据库。
经典解决方案
| 方案 | 原理 | 适用场景 | 注意事项 |
|---|---|---|---|
| 互斥锁(分布式锁) | 只允许一个线程回源查询,其他线程等待或快速失败 | 并发量极高 | 需考虑锁超时、死锁问题;可能增加响应延迟 |
| 逻辑过期 + 异步刷新 | 缓存永不过期,但设置逻辑过期时间;后台线程异步刷新 | 对一致性要求不苛刻 | 实现复杂;存在短暂数据不一致 |
| 热点 key 永不过期 | 对识别出的热点 key 不设置 TTL,由后台定时更新 | 热点可预测 | 需要维护热点列表;更新策略需谨慎 |
Kubernetes 环境下的锁选型:推荐使用 Redis Redlock 或 Etcd 分布式锁。注意锁的粒度要精确到 key 级别,避免锁住整个缓存分区。在 Pod 重启或扩缩容时,锁的持有者可能变化,需做好重试与超时处理。
2.3 缓存雪崩
定义:缓存中大量 key 同时过期,或者缓存节点发生大面积故障,导致大量请求直接打到数据库,引发数据库过载甚至宕机。
成因分析
- 批量过期:大量 key 设置了相同的过期时间(如统一 1 小时),在同一时刻集体失效。
- 缓存节点宕机:Redis 集群中某个分片或节点故障,导致该分片上的所有 key 不可用。
- 缓存重启:Kubernetes 中 Pod 重启或滚动更新导致缓存数据丢失。
经典解决方案
| 方案 | 原理 | 适用场景 | 注意事项 |
|---|---|---|---|
| 过期时间加随机值 | 在基础 TTL 上增加随机偏移(如 ±30%) | 所有场景 | 实现简单,效果显著 |
| 多级缓存 | 本地缓存(Caffeine/Guava)+ 分布式缓存(Redis) | 对延迟敏感 | 需处理本地缓存一致性问题 |
| 限流降级 | 对回源请求进行限流,保护数据库 | 高并发场景 | 需配合熔断机制,避免服务雪崩 |
| 缓存预热 + 高可用 | 提前加载热点数据;Redis 使用 Sentinel/Cluster 保障高可用 | 生产环境 | 预热策略需与业务对齐 |
2.4 三种异常的对比总结
| 特征 | 缓存穿透 | 缓存击穿 | 缓存雪崩 |
|---|---|---|---|
| 触发条件 | 查询不存在的数据 | 单个热点 key 过期 | 大量 key 同时过期 / 节点故障 |
| 影响范围 | 单个请求 | 单个热点 key | 全局或大面积 |
| 数据库压力 | 持续低量穿透 | 瞬间高并发 | 持续高并发 |
| 核心解决思路 | 拦截无效请求 | 控制并发回源 | 分散过期时间 + 高可用 |
| K8s 环境关注点 | Sidecar 布隆过滤器 | 分布式锁的 Pod 亲和性 | Redis 集群 Pod 反亲和 |
重要提醒:在 Kubernetes 环境下,缓存雪崩的风险更高。因为 Pod 滚动更新、HPA 弹性伸缩、节点故障等场景都可能导致缓存数据批量失效。建议在部署 Redis 时使用 StatefulSet + 持久化存储,并配置 Pod 反亲和策略,避免多个 Redis 实例调度到同一节点。