redis中的布隆过滤器

Scroll Down

概述

布隆过滤器专门是用来解决去重问题。它在起到去重作用的同时,在空间上还能节省90%以上。可以把布隆过滤器理解为一个不怎么精确的set结构,当使用contains方法判断某个对象是否存在时,它可能会误判。但是布隆过滤器也不是特别不精确,只要参数设置得合理,它的精确度也可以控制得相对足够精确,只会有小小的误判概率。

Redis中的布隆过滤器

当布隆过滤器说某个值存在时,这个值可能不存在;当它说某个值不存在时,那就肯定不存在。

布隆过滤器的基本用法

布隆过滤器有两个基本指令,bf.add和bf.exists。bf.add添加元素,bf.exists查询元素是否存在,它们的用法和set集合的sadd和sismember差不多。注意bf.add只能一次添加一个元素,如果想要一次添加多个,就需要用到bf.madd指令。同样如果需要一次查询多个元素是否存在,就需要用到bf.mexists指令。
布隆过滤器对于已经见过的元素肯定不会误判,它只会误判那些没有见过的元素。
上面使用的布隆过滤器只是默认参数的布隆过滤器,它在第一次add的时候自动创建。Redis其实还提供了自定义参数的布隆过滤器,需要在add之前使用bf.reserve指令显式创建。如果对应的key已经存在,bf.reserve会报错。bf.reserve有三个参数,分别是key、error_rate(错误率)和initial_size。
error_rate越低,需要的空间越大。
initial_size表示预计放入的元素数量,当实际数量超出这个数值时,误判率会上升,所以需要提前设置一个较大的数值避免超出误判率升高。
如果不使用bf.reserve,默认的error_rate是0.01,默认的initial_size是100。

注意事项

布隆过滤器的initial_size设置得过大,会浪费存储空间,设置得过小,就会影响准确率,用户在使用之前一定要尽可能地精确估计元素数量,还需要加上一定的冗余空间以避免实际元素可能会意外高出估计值很多。
布隆过滤器的error_rate越小,需要的存储空间就越大,对于不需要过于精确的场合,error_rate设置的稍大也没关系。

布隆过滤器的原理

每个布隆过滤器对应到Redis的数据结构里面就是一个大型的位数组和几个不一样的无偏hash函数。
所谓无偏就是能够把元素的hash值算得比较均匀,让元素被hash映射到位数组中的位置比较随机。
向布隆过滤器中添加key时,会使用多个hash函数对key进行hash,算得一个整数索引值,然后对位数组长度进行取模运算得到一个位置,每个hash函数都会算得一个不同的位置。再把位数组的这几个位置都置为1,就完成了add操作。
向布隆过滤器询问key是否存在时,跟add一样,也会把hash的几个位置都算出来,看看位数组中这几个位置是否都为1,只要有一个位为0,那么说明布隆过滤器中这个key不存在。如果这几个位置都是1,并不能说明这个key就一定存在,只是极有可能存在,因为这些位被置为1可能是因为其他的key存在所致。如果这个位数组比较稀疏,判断正确的概率就会很大,如果这个位数组比较拥挤,判断正确的概率就会降低。使用时不要让实际元素数量远大于初始化数量,当实际元素数量开始超出初始化数量时,应该对布隆过滤器进行重建,重新分配一个size更大的过滤器,再将所有的历史元素批量add进去。因为error_rate不会因为数量刚一超出就急剧增加,这就给我们重建过滤器提供了较为宽松的时间。

空间占用统计

布隆过滤器的空间占用计算有一个简单的计算公式。
布隆过滤器有两个参数,第一个是预计元素的数量n,第二个是错误率f。公式根据这两个输入得到的两个输出,第一个输出是位数组的长度1,也就是需要的存储空间大小(bit),第二个输出是hash函数的最佳数量k。hash函数的数量也会直接影响到错误率,最佳的数量会有最低的错误率。

K = 0.7 * (1/n) #约等于
f = 0.6185^(1/n)  # ^表示次方计算,也就是math.pow