目标
-
实践基于内存的布隆过滤器
参考:大白话布隆过滤器
布隆过滤器
参考:百度百科-布隆过滤器
布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。
应用
网页URL的去重,垃圾邮件的判别,集合重复元素的判别,查询加速(比如基于key-value的存储系统)等。
我的理解
布隆过滤器用于鉴别一个元素是否一定不存在或者可能存在。占用内存资源很少,并且查询效率高效。
计算布隆过滤器的容量可以计算,Bloom Filter Calculator
可以看到存入100万,误差在0.001,需要的size 大小在 1775.07KB ,只有1.73M。要求的误差越小,对cpu和内存的消耗越高。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tNGhSmPN-1601430760248)(https://s1.ax1x.com/2020/06/24/Nan5gH.png)]](https://i-blog.csdnimg.cn/blog_migrate/3ab18b275dcc1674842e4064a72928b3.png#pic_center)
实践
基于 spring boot cache 和 goole guava 包
项目地址:https://github.com/gengzi/codecopy
具体代码:fun.gengzi.codecopy.business.product 下的代码
思路:
两个方法:
一个是将所有缓存的key加载到内存的布隆过滤器,供查询数据时使用
一个是鉴别布隆过滤器是否包含该缓存的key,不包含,直接响应,包含,查询数据库返回数据。
引入 goole guava 依赖
guava 提供了布隆过滤器
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
业务类
fun.gengzi.codecopy.business.product.service.impl.ProductCacheServiceImpl
关于 BloomFilter 的用法,可以查看下api
// 存放的数量
private static int size = 1000000;
// 创建布隆过滤器
private static final BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size);
// 将缓存的key,设置到布隆过滤器中
@Override
public void putBloomKey() {
List<Integer> allId = productDao.getAllId();
allId.forEach(id -> {
bloomFilter.put(id);
});
}
/**
* 根据产品id 获取产品信息
* <p>
* 使用布隆过滤器,判断id 是否存布隆过滤器中 存在
* 查缓存,如果有,返回
* 无,直接返回
*
* @param id
* @return
*/
@Cacheable(cacheManager = "loclRedisCacheManagers", value = "PRODUCT_INFO_ID_NOPREFIX", key = "#id", cacheNames = {"PRODUCT_INFO_ID_NOPREFIX"})
@Override
public Product getOneProductCacheInfoBloom(Integer id) {
// 如果存在返回 true ,不存在返回 false
boolean isContain = bloomFilter.mightContain(id);
if (isContain) {
return productDao.findById(id.longValue()).orElseThrow(() -> new RrException("error ", RspCodeEnum.FAILURE.getCode()));
} else {
throw new RrException("error ", RspCodeEnum.FAILURE.getCode());
}
}
controller 层
在本例中,需要手动的执行,将key存入到布隆过滤器中,实践中,可以考虑在上线后,在运维页面,手动初始化一下布隆过滤器数据。
@ApiOperation(value = "将key存入布隆过滤器", notes = "将key存入布隆过滤器")
@ApiResponses({@ApiResponse(code = 200, message = "\t{\n" +
"\t \"status\": 200,\n" +
"\t \"info\": {\n" +
"\t }\n" +
"\t \"message\": \"信息\",\n" +
"\t}\n")})
@PostMapping("/putBloomKey")
@ResponseBody
public ReturnData putBloomKey() {
productCacheService.putBloomKey();
ReturnData ret = ReturnData.newInstance();
ret.setSuccess();
return ret;
}
@ApiOperation(value = "缓存穿透-使用布隆过滤器(Redis)", notes = "缓存穿透-使用布隆过滤器(Redis)")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "id", required = true)})
@ApiResponses({@ApiResponse(code = 200, message = "\t{\n" +
"\t \"status\": 200,\n" +
"\t \"info\": {\n" +
"\t }\n" +
"\t \"message\": \"信息\",\n" +
"\t}\n")})
@PostMapping("/findCacheUseRedisBloomByErrorId")
@ResponseBody
public ReturnData findCacheUseRedisBloomByErrorId(@RequestParam("id") Integer id) {
Product oneProductCacheInfo = productCacheService.getOneProductCacheInfoRedisBloom(id);
ReturnData ret = ReturnData.newInstance();
ret.setSuccess();
ret.setMessage(oneProductCacheInfo);
return ret;
}

本文介绍了布隆过滤器的概念及其在解决缓存穿透问题中的应用。通过使用Google Guava库实现了一个基于SpringBoot Cache的布隆过滤器示例,用于在查询数据时判断缓存key是否存在,减少无效数据库查询。实践步骤包括创建布隆过滤器、存储缓存key、判断key是否存在,并提供了手动初始化布隆过滤器的接口。
445

被折叠的 条评论
为什么被折叠?



