什么是缓存穿透?如何解决?
简化版
缓存穿透指请求查询的数据在缓存和数据库里都不存在,导致每次请求都绕过缓存打到数据库。常见原因是恶意请求不存在的 ID,或者业务里确实有大量空结果查询。解决方案是参数校验、缓存空值、布隆过滤器,以及对异常流量做限流和风控。
详细版
缓存穿透的关键是“数据不存在”。普通缓存只有查到数据才写缓存;如果数据库查不到,不写缓存,那么下次同样查询还是会打到数据库。
常见方案:
| 方案 | 原理 | 优点 | 注意点 |
|---|---|---|---|
| 参数校验 | 非法 ID、格式错误直接拦截 | 成本最低 | 只能挡明显非法请求 |
| 缓存空值 | DB 查不到也缓存一个空标记 | 简单有效 | TTL 要短,防止新数据被空值挡住 |
| 布隆过滤器 | 先判断 key 是否可能存在 | 内存小、挡随机 key 攻击 | 有误判,需维护过滤器 |
| 限流风控 | 对异常来源限速或封禁 | 抗攻击 | 需要监控和策略 |
缓存空值适合同一个不存在 key 被反复查;布隆过滤器适合大量随机不存在 key 的攻击。
完整版教学
一、穿透为什么会打到数据库
缓存命中的前提是缓存里有值。正常商品 ID 第一次查库后会回填缓存,后续请求都走缓存。但如果用户查询 id=-1 或一个根本不存在的商品 ID,数据库返回空,很多代码不会把空结果写入缓存。于是下一次同样请求还是缓存未命中,继续查数据库。
如果只是少量请求,问题不大;如果攻击者批量构造不存在 ID,每个请求都绕过缓存,数据库就被迫处理大量无意义查询。这就是穿透。
二、参数校验是第一道门
很多穿透请求其实在业务入口就能挡掉。比如用户 ID 必须是正整数,手机号必须符合格式,订单号长度固定。如果连格式都不合法,就不应该进入缓存和数据库查询链路。
参数校验成本最低,但只能挡明显非法数据。对于格式合法但业务上不存在的 ID,比如一个很大的商品 ID,仍然需要缓存空值或布隆过滤器。
三、缓存空值怎么用
缓存空值的做法是:数据库查不到时,也往 Redis 写一个特殊空标记,比如 NULL,并设置较短 TTL。下次同样 key 再来,缓存命中空标记,直接返回不存在,不再查数据库。
短 TTL 很关键。因为数据可能后来被创建出来,如果空值缓存时间太长,新数据会被旧空值挡住。通常空值 TTL 会比正常数据短很多。还要注意空间问题:如果攻击者构造海量不同 key,缓存空值会占用大量 Redis 内存,这时布隆过滤器更合适。
四、布隆过滤器适合挡随机攻击
布隆过滤器是一种概率型数据结构,用很小内存判断一个 key 是否可能存在。它的特点是:判断不存在一定可靠;判断存在可能误判。也就是说,如果布隆过滤器说某个商品 ID 不存在,就可以直接拒绝,不查缓存和数据库;如果说可能存在,再走正常查询。
布隆过滤器适合把全量合法 ID 提前加载进去,用来拦截随机不存在 ID。代价是要维护它:新增数据要同步加入过滤器,删除数据如果要求精确处理,需要重建或使用支持删除的计数布隆过滤器。
五、穿透和击穿、雪崩怎么区分
穿透查的是不存在数据;击穿查的是存在的热点数据,但缓存刚好过期;雪崩查的是存在数据,但大量 key 或缓存服务同时失效。三者的根因和解法完全不同。
穿透偏安全和过滤,击穿偏热点保护,雪崩偏容量和高可用。面试时先用“数据是否存在”和“影响范围是单 key 还是多 key”切开,答案就很清楚。
六、面试追问与工程边界
面试官常会追问“布隆过滤器误判怎么办”。布隆过滤器只会把不存在的数据误判为可能存在,不会把存在的数据误判为不存在。因此误判最多导致少量请求继续查缓存和数据库,不会错杀真实数据。误判率可以通过位数组大小和哈希函数个数控制。
还要注意数据变更。如果新商品、新用户不断创建,布隆过滤器要同步追加;如果删除很多数据,普通布隆过滤器不支持精确删除,可能需要定期重建或使用计数布隆过滤器。布隆过滤器适合挡攻击,不适合承担唯一的数据正确性判断。
七、常见误区与追问
这道题不能只背概念,要把「缓存穿透」放回真实分布式系统里解释:参与方是谁、状态怎么流转、失败后怎么恢复,以及它在一致性、性能、可用性之间做了什么取舍。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心结论 | 缓存穿透是查询不存在的数据导致请求绕过缓存打到数据库,常用空值缓存和布隆过滤器治理 | 不要停在名词解释 |
| 流程机制 | 请求查询 key -> 缓存未命中 -> 数据库也查不到 -> 写入短 TTL 空值或布隆过滤器拦截 -> 后续请求在缓存层返回 | 说明触发方、存储方、确认点和兜底 |
| 工程取舍 | 不存在 userId=999999 若每秒 1000 次请求都查库,会直接把数据库打满;缓存空值 60 秒可挡住重复请求 | 缓存提升吞吐但会引入旧值、热点、内存和失效风暴问题 |
缓存穿透 面试拆解:
1. 请求查询 key
2. 缓存未命中
3. 数据库也查不到
4. 写入短 TTL 空值或布隆过滤器拦截
5. 后续请求在缓存层返回
记忆钩子:先说明缓存承担的读写压力,再拆穿透、击穿、雪崩、热点、一致性和淘汰策略;回答时要紧扣「缓存穿透」这道题,不要把相邻概念混成一段泛泛的分布式套话。
- 误区:缓存穿透就是缓存没命中。 关键是数据库也没有该数据,导致恶意或重复请求持续打库。
- 误区:布隆过滤器能准确判断存在。 布隆过滤器可判断一定不存在,存在判断可能有误判。
- 误区:空值缓存 TTL 可以很长。 过长会影响后续真实数据写入后的可见性。
- 追问:空值缓存有什么风险? 可能缓存大量无效 key,占内存,并造成短时间旧空值。
- 追问:布隆过滤器删除难怎么办? 可重建过滤器、使用计数布隆或结合短 TTL。
- 追问:接口层还要做什么? 参数校验、鉴权、限流,拦截明显非法 key。
八、加强记忆
缓存穿透的关键词是“不存在的数据”。缓存没有,数据库也没有,每次都打到 DB。治理顺序是入口参数校验先挡明显非法请求,同一个不存在 key 用缓存空值,大量随机不存在 key 用布隆过滤器,再用限流风控兜住攻击流量。穿透和击穿、雪崩最大的区别是:穿透查的数据本来就不存在。