缓存穿透防护方案:布隆过滤器与空值缓存策略整合
最近在优化后端服务的缓存一致性时,踩了一个大坑。项目中频繁出现缓存穿透问题,导致数据库压力过大。
问题复现步骤
- 高并发场景下,大量不存在的key请求直接打到数据库
- 数据库查询返回空结果,未做任何缓存处理
- 每次都走数据库查询,性能急剧下降
解决方案整合
我采用了布隆过滤器+空值缓存的双重防护策略:
// 布隆过滤器初始化
private final BloomFilter<String> bloomFilter = BloomFilter.create(
Funnels.stringFunnel(Charset.defaultCharset()),
1000000, // 预估容量
0.01 // 误判率
);
// 缓存读取逻辑
public String getData(String key) {
// 先用布隆过滤器检查是否存在
if (!bloomFilter.mightContain(key)) {
return null; // 直接返回,不查询数据库
}
String data = redisTemplate.opsForValue().get(key);
if (data == null) {
// 空值缓存处理
data = queryFromDB(key);
if (data == null) {
// 缓存空值,避免缓存穿透
redisTemplate.opsForValue().set(key, "", 5, TimeUnit.MINUTES);
} else {
// 正常缓存数据
redisTemplate.opsForValue().set(key, data);
// 同时加入布隆过滤器
bloomFilter.put(key);
}
}
return data;
}
实践总结
- 布隆过滤器有效拦截99%以上的无效请求
- 空值缓存避免了缓存击穿问题
- 两种策略配合使用效果更佳,但要注意内存占用和维护成本

讨论