基于redis的布隆过滤器

1 布隆过滤器
1.1 说明

百度百科里面对布隆过滤器的说明如下:

如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。[链表],树等等数据结构都是这种思路. 但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢(O(n),O(logn))。不过世界上还有一种叫作散列表(又叫[哈希表],Hash table)的数据结构。它可以通过一个[Hash函数]将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。

举一个例子,有一个长度为8位的字节数组

0|0|0|0|0|0|0|0

字符串tom通过hash计算得到3,5,那么修改位置3和位置5

0|0|0|1|0|1|0|0

字符串jack通过hash计算得到0,1,3

1|1|0|1|0|1|0|0

字符串kk通过hash计算得到1,3,5 但是1,3,5的位置已经被别人填充过了,那么如果判断1,3,5的位置是否都是1,标识存在kk,就会出错,实际上kk没有存进去,存进去的是tom和jack,就会出现一定的误差.

如果现在对字符串mm进行hash,得到6,7,现在6,7的位置都是0,那么mm肯定不存在改字节数组中.

1.2 结论

所以得到布隆过滤器的两个特性

1 如果判断不存在布隆过滤器中,那么就一定不存在 .例如mm

2 如果存在布隆过滤器中,那么可能不存在 .例如kk

1.3 应用

那么布隆过滤器该如何应用呢

1 爬虫的url去重: 一般我们爬虫通过递归得到的url很容易重复,比如首页可能有一个个人中心的超链接,个人中心的页面有一个首页的超链接,但是我们并不想重复爬取已经爬过的界面,如果用键值对的方式对于大网站很多url就会容易内存泄漏,使用布隆过滤器的方式虽然有一定的误判率,但是基本满足需求

2 缓存击穿: 面试经常提到的redis缓存击穿,如果查询一个数据库不存在的key,因为数据库也没有,redis也没有缓存上,那么请求很多就会直接怼到数据库上,造成缓存击穿.如果使用布隆过滤器先把所有的key放进去,如果不存在布隆过滤器中,那么数据库中肯定也不存在,可以直接返回给用户,不用数据库再查一遍了

2 redis安装布隆过滤器模块
2.1 说明

为什么redis适合用来做布隆过滤器呢,主要是因为redis有一个Bitmaps的结构,可以理解为以位为单位的数组,每个位只能存储0和1,例如

0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0

redis最大能够存储512M的Bitmaps

1M = 1024k

1k = 1024字节

1字节 = 8位

所以 512M = 512x1024x1024x8 = 4294967296,可以存储40多亿的位数

2.2 安装
# 默认redis已经安装好了 文章redis是安装在/usr/local/redis下的
# 进入到redis目录
cd /usr/local/redis
# 创建module文件夹
mkdir module && cd module
# 如果没有安装git 可以安装一下git
yum -y install git
# 克隆redisbloom源文件
git clone https://github.com/RedisLabsModules/redisbloom.git
# 进入redisbloom文件夹
cd redisbloom/
# 编译
make
# 编译后会得到一个redisbloom.so的文件 然后在redis.conf增加如下配置
loadmodule ../module/redisbloom/redisbloom.so
# 重启redis
pkill redis
/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf
2.3 布隆过滤器的基本命令
bf.reserve(key, error_rate, capacity)
● key 键
● error_rate 错误比例,该比例越小,则需要的空间越大
● capacity 容量,当实际容量超过这个数的时候,误判率会增加

bf.add(key, value)
● key 键
● value 值

bf.exists(key, value)
● key 键
● value 值
3 java实现基于redis的布隆过滤器
3.1 初始化布隆过滤器
public boolean initBloomFilter(String key,Long errorRate,Integer count){
    String script = "redis.call('bf.reserve', KEYS[1], ARGV[1], ARGV[2])";
    RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
    Object result = redisTemplate.execute(redisScript,genericToStringSerializer,genericToStringSerializer, Collections.singletonList(key),errorRate,count);
    if(result!=null && OK.equals(result.toString())){
        return true;
    }
    return false;
}
3.2 向布隆过滤器添加数据
public boolean addBloomFilter(String key,String value){
    String script = "redis.call('bf.add', KEYS[1], ARGV[1])";
    RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
    Object result = redisTemplate.execute(redisScript,genericToStringSerializer,genericToStringSerializer, Collections.singletonList(key),value);
    if(SUCCESS.equals(result)){
        return true;
    }
    return false;
}
3.3 判断布隆过滤器是否存在数据
public boolean existBloomFilter(String key,String value){
    String script = "redis.call('bf.exists', KEYS[1], ARGV[1])";
    RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
    Object result = redisTemplate.execute(redisScript,genericToStringSerializer,genericToStringSerializer, Collections.singletonList(key),value);
    if(SUCCESS.equals(result)){
        return true;
    }
    return false;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值