上周值班,凌晨两点被报警电话叫醒。监控大屏上,订单服务的错误率从 0.1% 直接飙到了 87%,数据库连接池被打满,Redis 的 CPU 饱和到 100%。重启一遍 Redis,三分钟后又是同样的景象。最后查清楚了:一批羊毛党脚本拿着根本不存在的商品 ID 疯狂刷接口,缓存层挡不住,请求全部穿透到底层 MySQL。这就是 Redis 缓存治理里最典型的两个杀手——缓存穿透和缓存雪崩。这两个问题要是没提前做好防线,平时看着没事,流量一上来直接教做人。
这篇文章我把缓存穿透和缓存雪崩从原理到实战完整拆一遍,包括空值缓存、布隆过滤器、过期时间打散、多级缓存这些方案怎么选、怎么做、代码怎么写,最后附上我自己踩坑总结的排查速查表。不管是刚上手 Redis 的初学者,还是正在做缓存治理的中间件开发,都应该能在这篇里找到可以直接复用的东西。
1. 先把问题定义清楚:穿透和雪崩到底在说什么
很多文章把缓存穿透、缓存击穿、缓存雪崩三个词混着讲,概念越看越糊。我建议先把三个场景分清楚,因为它们的诱因完全不同,解决方案也完全不一样。
1.1 缓存穿透:查了一个根本不存在的数据
缓存穿透指的是,请求的数据在缓存里没有,在数据库里也没有,结果就是每次请求都必须打到数据库。正常流程是:先查 Redis,没有就查 MySQL,查到了回填缓存。可如果一个 key 对应的数据本来就不存在,那缓存里永远不会有这个 key,所有的请求都会穿透 Redis 直接打到数据库。单个请求没什么问题,但如果是大量请求都在查同一个不存在的 key,或者攻击者故意构造大量不存在的 key,数据库的压力就会瞬间爆表。
用生活里的例子类比:你开了一家面馆,门口有个小黑板写着今日菜单。正常客人看黑板点菜,厨师按菜单做。可有个捣乱的客人,每天专门点那些菜单上根本没有的菜。服务员每次都得到后厨问一遍"有没有这道菜",后厨每次都白忙活。黑板上的菜单永远帮不上忙。
1.2 缓存雪崩:大量 key 在同一时间集体失效
缓存雪崩是指,缓存里的大量 key 在同一时间段内集中过期,或者 Redis 整个服务宕机,导致海量请求绕过缓存直接打到数据库。数据库扛不住,系统就崩了。
这两个问题最大的区别是:穿透是"数据本身不存在",雪崩是"数据存在但缓存没接住"。前者是结构性问题,后者是时间点或可用性问题。
1.3 为什么这两个问题在 Redis 架构里特别容易出
Redis 作为中间件,承担的是流量缓冲区的角色。MySQL 能承受的并发一般也就几百到一两千,Redis 单机就能扛 8 万到 10 万 QPS。这中间差了两个数量级。缓存层一旦失效,所有流量就打回原来的数据库,这种量变到质变的瞬间,就是系统雪崩的根源。所以缓存治理的核心思路,不是让数据库变强,而是想尽一切办法让"数据库被请求打穿"这件事不发生。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 缓存穿透的深度拆解:为什么空值和恶意 key 能击垮业务
缓存穿透看着简单,但实际发生的时候排查起来并不容易。这里把原理和最常见的几个诱因拆开来讲。
2.1 一次订单服务被打挂的现场还原
那天凌晨的订单服务,被攻击的并不是真实的商品 ID,而是大量随机生成的、形如 -1 和 99999999 之类的非法 ID。这些 ID 在数据库里完全查不到,所以每次请求都触发 full query,MySQL 的 rows examined 疯狂上涨,磁盘 IO 被打满,连接池耗尽后,正常用户的请求也全部阻塞。
我后来在复盘时看到监控数据:短短 30 分钟内,MySQL 的 QPS 从平时的 3000 涨到 12 万,平均每条 SQL 扫描行数超过 10 万行。这种量级下,任何连接池优化都是杯水车薪,唯一的出路是让这些请求根本到不了数据库。
2.2 缓存穿透的三个常见诱因
实际业务里,穿透不全是恶意攻击,很多时候是我们自己代码的漏洞:
恶意攻击或爬虫。攻击者构造大量不存在的 key 定向轰炸,目的是拖垮数据库。这种请求的频率和规律性很强,监控里很容易看出异常波形。
业务数据本身存在空窗口。比如用户刚注册还没生成首页推荐数据,或者后台管理系统录入延迟,前端已经把请求发过来了。此时数据在库里就是没有,缓存也自然没有。
代码逻辑没有做回填。缓存里没有,数据库里也没有,代码就直接返回 null,完全没有把"null 这种情况也缓存起来"的处理。这是最常见的低级问题。
2.3 穿透的代价不只是数据库压力
穿透最直接的后果是数据库被打挂,但还有其他隐性成本。MySQL 每秒处理不了太多请求,一旦超负荷,连接池排队、超时、主从延迟全部接踵而至,连锁反应。另一方面,Redis 在穿透请求面前也很受伤,因为穿透请求照样会先打一遍 Redis,白白消耗 CPU 和内存。所以穿透防护要同时在入口层和缓存层做拦截,不能只靠数据库端死扛。
3. 防穿透方案实战:空值缓存、布隆过滤器与入口校验
穿透的解决方案分三层:入口层拦掉明显的垃圾请求,缓存层让"不存在"这件事本身也有缓存可用,必要时用布隆过滤器做更严格的过滤。
3.1 第一道防线:参数校验与非法请求拦截
最简单的防护往往效果最好。在进 Redis 之
