布隆过滤器预防缓存穿透
缓存穿透常用解决方法:
- 缓存空对象——当数据库没有查到的时候,也向redis中插入null的<key,value>
- 优点:实现简单,维护方便
- 缺点:额外的内存消耗,可能造成短期的不一致
- 布隆过滤
- 优点:内存占用啥哦,没有多余的key
- 缺点:实现复杂,可能存在误判
1. 布隆过滤器配置
@Configuration
public class BloomFilterConfig {
@Bean
public BloomFilter<Integer> bloomFilter() {
// 初始化布隆过滤器,预计插入100000个元素,误报率为0.01
return BloomFilter.create(Funnels.integerFunnel(), 100000, 0.01);
}
}
2. 防止缓存穿透
controller层
@GetMapping("/data/{key}")
public AjaxResult getData(@PathVariable Integer key) {
return getDateService.getDateFromCache(key);
}
service层
//如果布隆过滤器判断存在,则放行,这个请求会去访问redis,哪怕此时redis中的数据过期了,但是数据库里一定会存在这个数据
@Override
public AjaxResult getDateFromCache(Integer key) {
// 检查布隆过滤器中是否存在该key,存在则放行
if (!bloomFilter.mightContain(key)) {
log.info("Key {} does not exist in bloom filter", key);
return AjaxResult.error("获取数据失败"); // 布隆过滤器判定该key不存在
}
// 从Redis缓存中获取数据
log.info("Retrieving data for key {} from Redis", key);
Object data = redisTemplate.opsForValue().get("Document:" + key.toString());
//redis 中也没有得到数据,说明在数据库中
if(data == null){
log.info("Data not found in Redis for key {}. Fetching from DB...", key);
data = documentMapper.selectById(key);
//从数据库取出来后,放入redis和布隆中
// 将key添加到布隆过滤器
bloomFilter.put(key);
log.info("Added key {} to bloom filter", key);
// 将数据存入Redis缓存
redisTemplate.opsForValue().set("Document:"+ key, data);
log.info("Data {} stored in Redis", data);
}
return AjaxResult.success(data);
}
3. 数据初始化
将数据的id加入到布隆过滤器中,同时将要缓存的数据加入到redis中
@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行
@Component
@RequiredArgsConstructor
public class DataInitializer {
private final BloomFilter<Integer> bloomFilter;
private final RedisTemplate<String, Object> redisTemplate;
private final IDocumentService documentService;
@PostConstruct
public void init() {
List<Document> documents = documentService.list(null);
for (Document document : documents) {
// 将数据加入布隆过滤器
bloomFilter.put(document.getId());
// 将数据加入Redis
String key = "Document:" + document.getId();
redisTemplate.opsForValue().set(key, document);
}
}
}
4. 测试
1. 已经存储在redis中
2. key不存在布隆过滤器中
3. key在布隆过滤器中,但是redis中数据过期