Redis 缓存穿透怎么解决:布隆过滤器实战与代码示例

Redis 缓存穿透是指用户请求了缓存和数据库中都不存在的数据,导致每次请求都会直达数据库,严重时可能压垮 DB。解决这一问题的核心思路在于拦截无效请求,主流方案包括将空对象存入缓存、使用布隆过滤器预校验以及增加访问限流。我会根据业务对数据一致性要求的不同,组合使用这些策略来构建防御体系。
穿透现象与定位经验
我在一次大促演练中曾遇到过一个典型场景:监控大屏显示某核心订单接口 QPS 突然飙升,但业务量并没有增长,反而数据库连接池很快打满,出现大量超时错误。排查日志时我发现,攻击者或爬虫正在频繁请求 ID 为负数或超大随机整数的订单,这些数据在 MySQL 中根本不存在。

常规做法是查到数据为空时,依然把空值写入 Redis,设置较短的过期时间(如 5 分钟)。虽然简单,但如果恶意请求量巨大,缓存层会被大量无用的空对象撑爆,浪费内存且无法从根本上阻挡对后端的冲击。因此,我们需要更前置的拦截手段。
布隆过滤器的原理与选型
布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,它可以告诉你某个元素“一定不存在”或“可能存在”。对于缓存穿透防护来说,我们只需要关注“一定不存在”的情况,因为误判(把存在的数据说成不存在)只会导致性能轻微下降(回源查库),而不会造成数据错误。
其原理是利用一个二进制向量和多个哈希函数。当插入一个 Key 时,通过 K 个哈希函数计算出 K 个位点并置为 1;查询时,如果这 K 个位点有一个为 0,则肯定不存在;如果全为 1,则可能存在。
在实际落地时,我推荐使用 Google 开源的 Guava 库中的 BloomFilter 类,或者在 Redis 场景中集成 RedisBloom 模块。如果项目依赖较少,手写一个基于 BitSet 的简易版也很方便。关键点在于初始化时要预估数据量 N 和期望的误判率 P,从而计算出合适的位数组长度 m 和哈希函数个数 k。
工程落地代码示例
下面给出一个基于 Guava 的最小可运行示例,模拟判断用户 ID 是否有效的逻辑。你可以直接复制到项目中测试:
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
public class BloomFilterDemo {
// 预估容量 1000 万,误判率 3%
private static final BloomFilter<Integer> bloomFilter =
BloomFilter.create(Funnels.integerFunnel(), 10_000_000, 0.03);
public static void main(String[] args) {
// 初始化:将所有存在的合法用户 ID 加入布隆过滤器
for (int i = 1; i <= 10_000_000; i++) {
bloomFilter.put(i);
}
// 测试:查询一个不存在的 ID
int invalidId = -1;
if (bloomFilter.mightContain(invalidId)) {
System.out.println("可能存在,去查 Redis/DB");
} else {
System.out.println("肯定不存在,直接返回空,拦截穿透");
}
}
}
在生产环境中,通常会结合 Spring Cache 或自定义 AOP 切面,在查询缓存前先调用 bloomFilter.mightContain(key)。只有返回 true 时,才继续执行后续的缓存读取逻辑;返回 false 则直接返回 null 或默认值,彻底阻断对后端存储的查询。
注意事项与常见问题
使用布隆过滤器时,有几个坑我必须提醒你,这些都是我在复盘其他项目故障时总结出来的:
- 无法删除元素:标准的布隆过滤器不支持删除操作,因为位重叠可能导致误删其他元素的数据。如果需要动态删除,可以使用计数布隆过滤器(Counting Bloom Filter),但会占用更多内存。
- 内存占用评估:虽然布隆过滤器比 HashTable 省内存,但千万级数据量下依然需要数百 MB 内存。务必在生产环境前进行容量压测,避免 OOM。
- 数据同步延迟:如果数据库新增了数据,必须确保布隆过滤器能及时同步更新,否则新数据会被误判为不存在,导致短暂的缓存穿透失效。可以考虑定时全量重建或使用 Canal 监听 Binlog 增量同步。
通过上述方案,我可以有效地将绝大部分恶意无效请求拦截在 Redis 之外,保护后端数据库的安全。缓存穿透只是 Redis 常见故障之一,后续我们可以继续探讨缓存击穿和缓存雪崩的应对策略。
推荐阅读:《Redis 缓存击穿与雪崩的高可用架构设计》
相关阅读
本文首发于 Redis 缓存穿透怎么解决:布隆过滤器实战与代码示例 — https://lyxq.com.cn/en/blog/redis-cache-penetration-solution
转载或引用请注明出处,商业使用请联系作者获得授权。