缓存穿透是大量查询不存在的key导致请求击穿缓存直压数据库,引发雪崩;布隆过滤器可前置拦截非法key,但需配合空值缓存、格式校验和限流才能有效防御。

缓存穿透是什么,为什么它能打垮 Redis
缓存穿透是指大量请求查询根本不存在的 key(比如恶意构造的随机 ID、已下线商品 ID),这些请求全部击穿缓存,直接压到后端数据库。当并发稍高,数据库连接池迅速耗尽,响应变慢甚至超时,上游服务重试加剧压力,最终形成雪崩——Redis 本身虽不宕机,但整个链路因数据库拖垮而“等效宕机”。
常见现象包括:redis.clients.jedis.exceptions.JedisConnectionException 频发、DB CPU 100%、监控里 cache hit rate 掉到 5% 以下。
布隆过滤器不是银弹:它只做「存在性粗筛」,允许误判(说存在,其实可能不存在),但绝不漏判(说不存在,那一定不存在)。所以它适合放在缓存之前,拦截 99% 的非法 key 请求,让真正需要查 DB 的请求大幅减少。
Java 中用 RedisBloom + Jedis 快速接入布隆过滤器
Redis 自 6.2 起原生支持 BF.RESERVE、BF.ADD、BF.EXISTS 等命令,但生产环境更推荐用官方模块 redisbloom(需单独加载);若无法改 Redis 配置,可用客户端侧布隆过滤器(如 guava 或 redisson 的 RBloomFilter)。
实操建议:
- 优先用服务端布隆过滤器(
redisbloom),避免本地内存膨胀和多实例不一致 - 初始化时用
BF.RESERVE myFilter 0.01 1000000:错误率设为0.01(1%),预估容量1000000个元素;错误率越低,内存占用越高 - 写入时务必在新增合法数据后,同步调用
BF.ADD myFilter valid_key;漏加 = 白屏风险 - 读请求流程必须是:
BF.EXISTS myFilter key→ 返回0则直接返回空,不查 Redis/DB;返回1才走正常缓存逻辑
绕过布隆过滤器的典型坑点 布隆过滤器容易被当成“一劳永逸”的开关,但实际落地常踩几个硬伤:
常见错误现象:
-
BF.EXISTS返回1,但 DB 里真没这条记录 → 布隆过滤器没及时更新,或写 DB 和写布隆不同步(比如事务回滚后布隆没回退) - 上线后发现大量
key被误判为不存在 → 初始容量预估太小,扩容只能重建,旧 filter 无法 resize - 用
guava BloomFilter存在本地内存,集群多实例间不同步,A 实例加了 key,B 实例查不到 → 直接失效 - 对
key做了 MD5 或其他哈希再进布隆,但查询时忘了同样哈希 → 永远查不到
关键判断:只要业务有「数据删除」或「ID 生成规则变更」,就必须配套清理或重建布隆过滤器;否则它会成为脏数据放大器。
布隆过滤器扛不住的场景,得靠组合策略
布隆过滤器对「已知非法模式」无效,比如攻击者用脚本遍历 user:1~user:9999999,这些 key 可能全在布隆里(如果用户表真实存在这么多 ID)。此时单靠布隆没用。
必须叠加的防御手段:
- 对高频访问的非法
key做「空值缓存」:查 DB 确认不存在后,往 Redis 写user:123456→null,并设短 TTL(如 60s),防止缓存雪崩 - 接口层加参数校验:ID 必须符合正则
^u_[a-z0-9]{8,16}$,非法格式直接 400,不进缓存链路 - 限流兜底:用
redis-cell或网关层令牌桶,对单 IP / 单 UID 的GET /user/{id}接口限流,超过阈值直接拒掉


















