Redis学习(10)--布隆过滤器

212 阅读4分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路。

层峦叠嶂-布隆过滤器 >=4.0

背景: 讲个场景,比如我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,而他每次推荐都要去重,而如果用set结构去实现,则需要浪费的空间很大很大。布隆过滤器(Bloom Filter)就是为了专门解决这个问题的额,它相当于一个不怎么精确的set结构,当你使用它的contains方法判断某个对象是否存在时,他可能会误判。即当布隆过滤器说某个值存在时,这个值可能不存在;当它说某个值不存在时,那就肯定不存在。

使用方法

redis4.0之后提供了布隆过滤器插件

用docker安装体验Redis 4.0的布隆过滤器

> docker pull redislabs/rebloom # 拉取镜像
> docker run -p 6379:639 redislabs/rebloom # 运行容器
> redis-cli # 连接容器中的Redis服务

基本用法

> bf.add codehole user1 # 添加一个元素
(integer) 1
> bf.exists codehole user1
(integer) 1
> bf.exists codehole user2
(integer) 0
> bf.madd codehole user2 user3 user4
1)(integer) 1
2)(integer) 1
3)(integer) 1
> bf.mexists codehole user2 user3 user5
1)(integer) 1
2)(integer) 1
3)(integer) 0
  • Redis提供了自定义的布隆过滤器,需要我们在add之前使用bf.reserve 指令显式创建.如果对应的key已经存在,bf.reserve会报错。bf.reserve有三个参数,分别是key,error_rate(错误率)和initial_size
  • error_rate 越低,需要的空间越大
  • initial_size表示预计放入的元素数量,当实际数量超出这个数值时,误判率会上升,所以需要提前设置一个较大的数值避免超出导致误判率升高
  • 如果不使用bf.reserver,默认的error_rate是0.01,默认的initial_size是100

布隆过滤器的原理

每个布隆过滤器对应到Redis的数据结构里面就是一个大型的位数组和几个不一样的无偏hash函数,如下图f,g,h就是这样的hash函数。所谓无偏就是能够把元素的hash值算的比较均匀,让元素被hash映射到位数组中的位置比较随机。 在这里插入图片描述

  • 向布隆过滤器中添加key时,会使用多个hash函数对key进行hash,算得一个整数索引值,然后对位数组长度进行取模运算得到一个位置,每个hash函数都会算得一个不同的位置。再把位数组的这几个位置都置为1,就完成了add操作。
  • 查询时同样的方法若有一位为0,则说明不存在
  • 使用时不要让实际元素数量远大于初始化数量,当实际元素数量开始超出初始化数量时,应该对布隆过滤器进行重建,重新分配一个size更大的过滤器,再将所有的历史元素批量add进去(这就要求我们在其他的存储器中记录所有的历史元素,因为error_rate不会因为数量刚一超出就急剧增加,这就给我们重建过滤器提供了较为宽松的时间)

空间占用估计

  • 布隆过滤器的空间占用计算有一个简单的计算公式,但是推导起来比较繁琐,这里就省去推导过程了,直接引出计算公式
  • 布隆过滤器有两个参数,第一个是预计元素的数量n,第二个是错误率f。公式根据这两个输入得到两个输出,第一个输出是位数组长度l,也就是需要的存储空间大小(bit),第二个输出是hash函数的最佳数量k。hash函数的数量也会直接影响到错误率,最佳的数量会有最低的错误率
k=0.7*(l/n) #约等于
f=0.6185^(l/n) #^ 表示次方计算,也就是math.pow

从公式中可以看出以下结论

  1. 位数组相对越长(l/n),错误率f越低,这个和直观上理解是一致的
  2. 位数组相对越长(l/n),hash函数需要的最佳数量也越多,影响计算效率
  3. 当一个元素平均需要1个字节(8bit)的指纹空间时(l/n=8),错误率大约为2%
  4. 错误率为10%时,一个元素需要的平均指纹空间为4.792个bit,大约为5bit
  5. 错误率为1%时,一个元素需要的平均指纹空间为9.585个bit,大约为10bit
  6. 错误率为0.1%时,一个元素需要的平均指纹空间为14.377个bit,大约为15bit

免费的在线布隆计算器