Skip to content
Redis 可用性与热点治理细节 · 第 2 篇 / 共 4 篇
领域数据与中间件
专题Redis 专题
当前序列Redis 可用性与热点治理细节
阅读位置第 2 篇 / 共 4 篇当前专题第 5 个序列 / 共 7 个序列

缓存穿透治理怎么做更稳

缓存穿透最容易被轻描淡写地总结成一句话:

“查不到的数据也缓存一下就行。”

这只能解决最浅的一层。真正线上遇到的穿透问题,往往来自下面几类情况:

  • 大量恶意或异常参数直接打到 DB
  • 业务 ID 本身就是稀疏的,空值数量巨大
  • 热点接口在缓存 miss 时回源风暴叠加
  • 某些不存在对象根本不应该进入数据库层

先说结论

  • 缓存穿透是“不存在的数据反复绕过缓存打到下游”,核心目标是让无效请求尽早被挡住
  • 空值缓存、布隆过滤器、参数校验、限流降级往往要组合使用
  • 不要把穿透、击穿、雪崩混成一个问题,它们的治理手段侧重点不同
  • 真正稳的治理策略,一定包含监控和降级,不只是一个缓存技巧

先把问题定义清楚

缓存穿透指的是:

  • 请求访问的数据在缓存里没有
  • 在数据库里也没有
  • 每次请求都会直接打穿到数据库

它和另外两个常见问题要分开看:

  • 缓存击穿: 热点 key 失效,瞬时大量请求回源
  • 缓存雪崩: 大量 key 同时失效或缓存整体不可用

穿透的关键词是“不存在的数据”。

为什么它危险

因为它天然绕开了缓存系统的保护能力。
如果有人持续用不存在的用户 ID、订单号、商品 ID 打接口,请求会稳定命中数据库。

一旦并发大起来,风险会表现为:

  • DB QPS 异常上涨
  • 慢 SQL 和连接数上升
  • 缓存命中率看似正常,但接口 RT 明显抖动
  • 业务层线程池开始排队

第一层治理: 参数前置校验

这是最容易被忽略、但性价比最高的一层。

例如:

  • 用户 ID 必须为正整数
  • 订单号必须满足固定长度和前缀规则
  • 枚举类参数必须在白名单内

如果参数本身就非法,就不该放进缓存和数据库层。

这一层的特点是:

  • 成本低
  • 延迟低
  • 能挡住大量明显异常流量

第二层治理: 空值缓存

当请求的数据确实合法,但数据库里不存在时,可以把“空结果”也短暂缓存起来。

例如:

text
key = user:123456789
value = NULL_PLACEHOLDER
ttl = 60s

这样后续相同请求在一段时间内就不会再打数据库。

空值缓存的优点

  • 简单直接
  • 对单个不存在对象的反复查询很有效

它的边界

  • 如果恶意请求 ID 非常离散,空值 key 会迅速膨胀
  • TTL 不能太长,否则真实数据刚创建后可能还会被旧空值挡住

所以空值缓存适合:

  • 非法请求不离散
  • 不存在对象重复访问概率高

第三层治理: 布隆过滤器

布隆过滤器适合“先判断这个对象大概率是否存在”。

它特别适合:

  • 用户 ID、商品 ID、配置 ID 这类可预先加载的主键集合
  • 数据量大,但希望在进入缓存前先挡掉明显不存在的请求

典型流程是:

  1. 先查布隆过滤器
  2. 如果明确不存在,直接返回
  3. 如果可能存在,再查缓存和数据库

它的优点

  • 内存占用小
  • 对大规模稀疏 ID 很有效

它的限制

  • 有误判率,只能说“可能存在”
  • 数据更新时要考虑同步问题
  • 不适合频繁删除且要求绝对准确的集合

第四层治理: 限流和降级

如果穿透流量已经演化成攻击或系统异常,仅靠缓存技巧不够。
必须配合接入层和业务层的保护机制:

  • 网关限流
  • IP / 用户维度封禁
  • 接口级熔断降级
  • 异常参数快速失败

一个常见误区是把穿透完全交给 Redis 处理。
实际上真正的第一道防线往往在 API 网关和业务网关。

一个更稳的组合方案

大多数业务系统可以按这个顺序落地:

  1. 参数合法性校验
  2. 热门对象走本地缓存 + Redis
  3. 不存在对象短 TTL 空值缓存
  4. 主键集合大的场景补布隆过滤器
  5. 网关和服务层加限流熔断

这套组合通常比“只上一个 BloomFilter”更稳,也更容易维护。

工程上要特别注意的坑

1. 空值 TTL 过长

真实数据刚创建时,用户仍然可能读到“空结果”,造成短暂不一致。

2. 布隆过滤器和真实数据不同步

如果布隆集合没及时更新,会把合法请求提前挡掉。

3. 只看缓存命中率,不看无效请求比例

缓存命中率高,不代表没有穿透。
你还要关注:

  • miss 中数据库未命中的比例
  • 某类接口 404 / 空结果比例
  • 参数异常率

4. 把穿透问题拖成 DB 问题才处理

很多团队直到数据库连接数打满才反应过来,实际上网关层早该挡掉了。

一个排查思路

当怀疑线上有缓存穿透时,可以顺着这条线查:

  1. 某接口 miss 率是否异常升高
  2. miss 后 DB 未命中比例是否很高
  3. 是否集中在某类非法或稀疏 ID
  4. 请求来源是否集中在某 IP、某用户、某渠道
  5. 当前是否已有空值缓存或布隆过滤器

这样才能区分到底是:

  • 正常业务流量结构变化
  • 上游参数异常
  • 恶意攻击

总结

缓存穿透治理的重点不是“记住几个术语”,而是把无效请求尽量拦在系统前面。

真正稳的方案通常不是单点技巧,而是:

  • 参数校验
  • 空值缓存
  • 布隆过滤器
  • 限流降级
  • 监控告警

这五层一起做,系统抗穿透能力才会真正上一个台阶。

延伸阅读相关文章优先当前专题,再补跨专题关联。
同一序列 · 顺着当前主线继续读Redis 延迟队列怎么做更合适适合把 Sentinel、穿透防护、延迟队列和地理位置搜索放在一起看。Redis 专题 · Redis 可用性与热点治理细节同一序列 · 顺着当前主线继续读Redis GEO 附近检索怎么设计适合把 Sentinel、穿透防护、延迟队列和地理位置搜索放在一起看。Redis 专题 · Redis 可用性与热点治理细节同专题其他序列 · Redis 高可用、过期与性能治理过期键删除策略为什么会影响 RT适合把复制、故障切换、槽迁移、内存碎片和冷启动预热放在一条 Redis 稳定性主线上看。Redis 专题 · Redis 高可用、过期与性能治理同专题其他序列 · Redis 高可用、过期与性能治理缓存预热和冷启动怎么设计更稳适合把复制、故障切换、槽迁移、内存碎片和冷启动预热放在一条 Redis 稳定性主线上看。Redis 专题 · Redis 高可用、过期与性能治理跨专题关联 · 同场景:基础学习慢 SQL 治理为什么不能只靠索引适合把大事务、DDL、回填、慢 SQL 治理和归档分层放进一条持续治理主线上看。MySQL 专题 · MySQL 变更治理与数据生命周期跨专题关联 · 同场景:基础学习状态 TTL 和大状态治理怎么做适合把 Checkpoint、watermark、两阶段提交和状态 TTL 放在一条 Flink 实时处理主线上看。Flink 专题 · Flink 状态治理与实时链路
继续阅读Redis 可用性与热点治理细节当前序列第 2 篇 / 共 4 篇当前专题第 5 个序列 / 共 7 个序列
往前看
上一篇Redis Sentinel 和主从故障切换怎么理解回到当前序列上一章上一序列Redis 进阶数据模型与业务场景从第 1 篇开始:Redis String、List、Set、ZSet 怎么做业务建模
往后看
下一篇Redis 延迟队列怎么做更合适继续当前序列下一章下一序列Redis 数据结构与命令使用边界从第 1 篇开始:Redis String 之外的结构什么时候更合适

把零散经验整理成可查、可复用、可持续更新的企业级知识门户