赞
踩
六、 Redis布隆过滤器与springboot的整合 (集合lua脚本)
导读:本博文介绍了布隆过滤器的使用场景,以及google布隆过滤器和redis布隆过滤器分别的使用方法。当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在
布隆过滤器是什么:
布隆过滤器(英语:Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
优点:
相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常数。另外,散列函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势
缺点:
但是布隆过滤器的缺点和优点一样明显。误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣
流量攻击:故意访问不存在的数据,导致程序不断访问DB数据库的数据(缓存穿透-解决方式将空值加到缓存中,防止大量攻击到db)
安全阻截:当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉(提前做ID数据判断是否是合格的)
思考:key:10000 10001 10002 10003 千万大集合,查找key是否在集合里面
java常用数据结构,判断key是否在集合中:
set map key,value list 有序get[0]、get[1];
list.contain (key)遍历数据,进行equals()比较,性能低
set.contain(key) hashcode比较,性能较高,64位大概会占用1G
map.get(key) hashcode比较,性能还行,会出现hashcode冲突问题
上述常用做法,用小数据量还可以,大数据量不是占用内存过高,就是性能很低
使用场景:
网页爬虫对URL的去重,避免爬取相同的URL地址;
反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱(同理,垃圾短信);
缓存穿透,将已存在的缓存放到布隆中,当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉。
通过google布隆过滤器存储会员数据:
1、程序启动时将数据放入内存中
2、google自动创建布隆过滤器
3、用户ID进来之后判断是否是会员
pom.xml
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>21.0</version>
- </dependency>
构建底库:
- private BloomFilter<Integer> bf;
-
- /***
- * PostConstruct 程序启动时候加载此方法
- */
- @PostConstruct
- public void initBloomFilter() {
- SysUserExample sysUserExample = new SysUserExample();
- List<SysUser> sysUserList = sysUserMapper.selectByExample(sysUserExample);
- if(CollectionUtils.isEmpty(sysUserList)){
- return;
- }
- //创建布隆过滤器(默认3%误差)
- bf = BloomFilter.create(Funnels.integerFunnel(),sysUserList.size());
- for (SysUser sysUser:sysUserList) {
- bf.put(sysUser.getId());
- }
- }

查询是否存在:
-
- /***
- * 判断id可能存在于布隆过滤器里面
- * @param id
- * @return
- */
- public boolean userIdExists(int id){
- return bf.mightContain(id);
- }
google缺点:
内存级别产物
重启即失效
本地内存无法用在分布式场景
不支持大数据量存储
这里引入redis的布隆过滤器
可扩展性Bloom过滤器,一旦Bloom过滤器达到容量,就会在其上创建一个新的过滤器
不存在重启即失效或者定时任务维护的成本基于goole实现的布隆过滤器需要启动之后初始化布隆过滤器缺点:需要网络IO,性能比基于内存的过滤器低
当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在
Redis布隆过滤器安装过程 自己构建一个bitMap
git在centos7下面的安装:
1、安装git,直接使用yum安装即可: yum -y install git 2、创建git用户,git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。 useradd -m -d /home/git -s /usr/bin/git-shell git 3、初始化git仓库 mkdir -p /data/git cd /data/git git init --bare project1.git chown git.git project1.git -R 4、创建免密钥 cd /home/git mkdir .ssh chmod 700 .ssh touch .ssh/authorized_keys chmod 600 .ssh/authorized_keys chown git.git .ssh -R
下载并编译模块:
$ git clone git://github.com/RedisLabsModules/rebloom $ cd rebloom $ make # 当前路径会生成一个rebloom.so文件
将Rebloom加载到Redis中,在redis.conf里面添加
loadmodule /usr/local/rebloom/rebloom.so
# 更改配置后重启Redis即可
上述的安装提到需要重启Redis,但是生产环境的Redis可不是你想重启就重启的。使用MODULE LOAD 不重启redis加载rebloom插件
MODULE LOAD /"rebloom.so的绝对路径"/redisbloom.so
命令:
添加元素:BF.ADD(添加单个)、BF.MADD(添加多个)、BF.INSERT(添加多个)
检查元素是否存在:BF.EXISTS(查询单个元素)、BF.MEXISTS(查询多个元素)
BF.ADD bloom redis
BF.EXISTS bloom redis#判断是否存在
BF.EXISTS bloom nonxist
bloomFilterAdd.lua
- local bloomName = KEYS[1]
- local value = KEYS[2]
-
- -- bloomFilter
- local result_1 = redis.call('BF.ADD', bloomName, value)
- return result_1
bloomFilterExist.lua
- local bloomName = KEYS[1]
- local value = KEYS[2]
-
- -- bloomFilter
- local result_1 = redis.call('BF.EXISTS', bloomName, value)
- return result_1
本博文因需通过浏览器验证添加效果,所以将新增通过id增加的放到controller中,正式开发过程中可根据业务项目启动中查询数据加到redis中或者每天定时更新redis过滤器值。
新增:
- private static final String bloomFilterName = "isVipBloom";
- @Resource
- private RedisService redisService;
-
- @RequestMapping("/bloom/redisIdAdd")
- public boolean redisidAdd(int id){
- return redisService.bloomFilterAdd(bloomFilterName,id);
- }
-
- @Autowired
- private RedisTemplate redisTemplate;
-
- public Boolean bloomFilterAdd(String filterName,int value){
- DefaultRedisScript<Boolean> bloomAdd = new DefaultRedisScript<>();
- bloomAdd.setScriptSource(new ResourceScriptSource(new ClassPathResource("bloomFilterAdd.lua")));
- bloomAdd.setResultType(Boolean.class);
- List<Object> keyList= new ArrayList<>();
- keyList.add(filterName);
- keyList.add(value+"");
- Boolean result = (Boolean) redisTemplate.execute(bloomAdd,keyList);
- return result;
- }
-

查询是否存在:
-
- @Resource
- private RedisService redisService;
-
- @RequestMapping("/bloom/redisIdExists")
- public boolean redisidExists(int id){
- return redisService.bloomFilterExists(bloomFilterName,id);
- }
-
- @Autowired
- private RedisTemplate redisTemplate;
- public Boolean bloomFilterExists(String filterName,int value){
- DefaultRedisScript<Boolean> bloomExists= new DefaultRedisScript<>();
- bloomExists.setScriptSource(new ResourceScriptSource(new ClassPathResource("bloomFilterExist.lua")));
- bloomExists.setResultType(Boolean.class);
- List<Object> keyList= new ArrayList<>();
- keyList.add(filterName);
- keyList.add(value+"");
- Boolean result = (Boolean) redisTemplate.execute(bloomExists,keyList);
- return result;
- }

效果验证:放入111 333 ,查询111存在 ,444不存在
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。