Redis 缓存穿透:四层防线各管一段,别指望一招制敌

举报
yd_297085602 发表于 2026/11/01 13:21:38 2026/11/01
【摘要】 缓存穿透是 Redis 使用里最经典的问题:请求的 key 在缓存和数据库里都不存在,每次请求都击穿缓存直达数据库。流量一大,数据库负载瞬间飙高;被恶意构造大量不存在的 key 时,就是一场标准的攻击。很多文章只讲“布隆过滤器”一个词就结束了。实际工程里穿透要分层防,四层防线各管一段,这篇按优先级讲清楚。一、先理解穿透的三个面孔第一种是自然穿透:业务里本来就存在“查一个不存在的东西”的场景。...

缓存穿透是 Redis 使用里最经典的问题:请求的 key 在缓存和数据库里都不存在,每次请求都击穿缓存直达数据库。流量一大,数据库负载瞬间飙高;被恶意构造大量不存在的 key 时,就是一场标准的攻击。

很多文章只讲“布隆过滤器”一个词就结束了。实际工程里穿透要分层防,四层防线各管一段,这篇按优先级讲清楚。

一、先理解穿透的三个面孔

第一种是自然穿透:业务里本来就存在“查一个不存在的东西”的场景。比如用户主页被注销、商品下架、文章删除。量不大,但天天有。

第二种是恶意穿透:攻击者拿随机 key 高频请求,比如把 id 换成负数、超大数、随机字符串。目标是打垮数据库。

第三种是设计缺陷穿透:代码先查缓存、未命中查库、查不到就返回——查库结果为空时什么也不写。空结果永远进不了缓存,同一个不存在 key 会打数据库无限次。这是最常见也最冤的一种。

二、第一层防线:空值缓存,性价比之王

针对第三种缺陷,解法直截了当:数据库也查不到时,给这个 key 缓存一个空标记,配一个短 TTL。

逻辑三行:先查缓存,未命中查库;库里有值,正常缓存长 TTL;库里没值,缓存空标记短 TTL(比如 60 到 300 秒),返回空。这样同一个不存在 key 的请求,一分钟内只会打一次数据库。

两个细节决定这层防线的质量。第一,空值 TTL 要短——数据可能后来被插入,TTL 太长会出现“明明已存在却返回不存在”的一致性问题。第二,如果业务在意插入后立刻可查,在写路径插入数据时顺手删掉这个 key 的空标记。

空值缓存解决自然穿透和设计缺陷穿透绰绰有余,但它有一个天然弱点:对恶意穿透里的海量随机 key 无能为力——每个随机 key 都是一次新的穿透,空标记存了也只是存了一堆垃圾。所以需要第二层。

三、第二层防线:布隆过滤器,海量不存在 key 的克星

思路反转:不是“查过了才缓存”,而是预先把所有合法 key 装进一个集合,请求先过集合——集合说不存在,就一定不存在,直接拒绝,缓存和数据库都不用碰。

这个集合就是布隆过滤器。它的特性是:说不存在就一定不存在,说存在则小概率误判。误判的少量请求放行到缓存和数据库,代价可接受。

工程上两个选择。其一,Redis 侧:部分 Redis 版本支持 Bloom 模块,直接用原生命令添加和查询,对应用透明。其二,应用侧:用客户端库在应用层实现,灵活性更好。

必须正视的三个限制:布隆过滤器不支持删除元素,key 集合动态变化的场景需要定期重建或用计数变体;容量和误判率要在创建时规划好,容量不足会导致误判率飙升;数据同步是运维负担——写入路径要双写过滤器,漏写会把合法请求误杀。

所以布隆过滤器不是默认选项,是“存在恶意穿透风险或不存在 key 的量级确实大”时才上的第二层。

四、第三层防线:入口校验,成本最低收益最快

所有攻击流量里,随机乱串的 key 在参数合法性检查面前不堪一击:id 必须是纯数字、长度范围固定、前缀符合业务格式、枚举值在白名单内。Controller 层加一行校验,一大片恶意流量根本到不了 Redis。

这层防线五分钟就能上线,应该最先做——但它只能拦“格式非法”的请求,攻击者若用合法格式的随机 id,还是穿透。所以它是分层体系里的一环,不是全部。

五、第四层防线:限流与监控,最后的安全网

前三层都漏过来的流量,靠限流保住数据库:单 key 维度限流、接口维度限流、异常熔断。

监控侧,Redis 实例本身要配告警:连接数、瞬时 QPS、带宽使用率。这里有个实战教训值得单独说——出现“缓存超时、连接堆积”的现象时,先看监控区分是穿透还是实例规格瓶颈。高 QPS 打在同一个 key 上(热 key)和大量不同 key 穿透,现象相似,病因完全不同:前者要拆热 key 或加本地缓存,后者才走四层防线。不区分就动手改代码,很容易越改越糟。

六、组合架构:一张全景图

把四层按请求路径串起来:

入口参数校验,拦掉格式非法的;
布隆过滤器(可选),拦掉确定不存在的;
Redis 查询,命中返回,未命中查库;
数据库查到,写缓存长 TTL;查不到,写空标记短 TTL;
最外围,限流和监控兜底。

日常业务用“空值缓存加入口校验”基本够;接口对外开放或被攻击过,再叠“布隆过滤器加限流”。

七、三条实践建议

第一,别为穿透过度设计。多数内部系统的穿透量很小,空值缓存一层就足够,布隆过滤器引入的同步成本可能比问题本身更贵。

第二,缓存雪崩和击穿是穿透的近亲,排查时一起想:穿透是“不存在的 key”,击穿是“热点 key 过期瞬间”,雪崩是“大量 key 同时过期”。三者解法分别是空值缓存、互斥锁重建、过期时间加随机抖动,混用解法是常见错误。

第三,上线任何一层防线后,用压测验证:构造一批不存在的 key 打流量,观察数据库 QPS 是否被压到预期水位。防线是否真的在工作,数据说了算。

缓存的意义是挡住数据库。把“不存在的请求”也挡在缓存这一层,这个防御才算完整。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。