Redis 布隆过滤器

版权归原作者所有。 本文最后更新于:2023年12月5日 凌晨

布隆过滤器

可以判断某个值是否在集合中

推荐系统去重

当压力很大时,频繁查询数据库就会有很大的压力,去重工作的性能就跟不上了

Bloom Filter

就是为了专门解决这个去重问题,在空间上还能节省90%以上的空间

只是稍微有那么点不准确,也就是有一定的误判概率

可以把布隆过滤器理解为一个不怎么精确的set结构,contains方法判断某个对方是否存在时,可能会误判,但是也不是特别不精准,只要设置参数合理,精确度也可以设置的足够精确,只会有小小的误判。

可以保证过滤掉用户已经看过的内容,但是没有看过的可能也会有极小一部分被过滤,但是大部分新内容进行准确精准识别,保证可以推荐给用户的内容是无重复的。

命令

bf.add k1 u1
bf.add k1 u2
bf.exists k1 u1
bf.madd k1 u3 u4 u5
bf.mexists k1 u3 u4 u5

错误率

误判率大约1%多一点,有点高,可以优化

redis提供了自定义参数的布隆过滤器,需要我们再add之前bf.reserve指令显示创建

如果key已经存在了,会报错

有三个参数 key error_rate((错误率) initial_size(预计放入的元素数量,当实际数量超出这个数值,误判率会上升,需要提前设置一个较大的数值避免超出导致误判率升高)
error_rate越低,需要的空间就越大

如果不显示创建,默认错误率为 0.01 默认initial_size 100

初始值设置的过大也会浪费存储空间,设置过小,会影响准确率,使用之前一定尽可能的估计元素数量,还需要加上一定量的冗余空间,避免实际元素意外高出估计值过多。

有些对于不需要过于精确的场景,错误率稍微大一点也无伤大雅。

原理

每个布隆过滤器对应Redis的数据结构就是一个大型的位数组和几个不一样的无偏hash函数

无偏就是能够把元素的hash值算的比较均匀,让元素被hash映射到位数组中的位置比较随机。

例如 hello 把 hello经过hash之后计算出hash值,通过hash值取模。得出的数值作为位数组的索引,找到对应数组位置之后,设置为1,这个时候完成了add操作

如果查询是否存在时,也是先hash进行取模获取到位数组的位置,然后判断是否为1,如果位0,则不存在,如果为1 不一定存在,因为h是index e是一个index,他是一个组合,所以,有可能很多种组合组成了一个hello的位置,造成了一个hello存在的假象,这个时候,就看出 如果这个index计算的越稀疏,就越准确,越稠密越不准确。(也就是说错误率越小,占用空间越大的原因)

使用时不能让实际元素数量大于初始化适量过大,当过于大时,应该重建过滤器,重新分配一个size更大的过滤器,再将所有历史元素add进去(这就要求在其他存储中存历史元素),因为error_rate不会因为数量刚已超出就急剧增加,给我们重建过滤器提供了较为宽松的时间。

空间占用估计

简单公式:

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

总结

1、位数组相对越长(1/n),错误率f越低。

2、位数组相对越长,hash函数需要的最佳数量也越多,影响计算效率。

3、当一个元素需要1个字节(8bit)的指纹空间时(1/n=8),错误率大约为2%。

4、错误率为10%时,一个元素需要平均指纹空间为4.792个bit,大约为5bit。

5、1%,9.585bit/个,~10bit

6、0.1%,14.377bit/个,大约为15bit

set和布隆过滤器 空间占用说明:

set是存储的元素内容,过滤器只是元素的指纹,所以,set占用4或者8字节,取决于32位或者64位系统,本身还需要一个指针倍set集合引用,指纹空间一个元素才需要不足2个字节,所以布隆过滤器空间占用还是很有优势的。

计算地址

计算麻烦,现有在线计算器可以计算

https://krisives.github.io/bloom-calculator/

实际元素超出时,误判率会如何变化?

通过公式可以计算出实际元素超出预计元素个数是,错误的变化情况?

f=(1-0.5^t)^k // 极限近似,k是hash函数的最佳数量

当t增大时,错误率f也跟着增大,分别选择错误率为10%,1%,0.1%的k值,

110%,倍数比为2时,错误率回升至接近40%, 这个值就比较危险了
21%,倍数比为2时,-> 15% , 也挺可怕
30.1%,倍数比为2时,-> 5%, 也是比较高的

布隆过滤器的其他应用

在爬虫系统中,我们需要对URL去重,已经爬过网页可以不用爬了,会错过少量页面。

还可以显著降低IO请求数量,通过过滤器过滤掉大量不存在row请求,然后再去磁盘查询。

过滤器也放在了邮件系统的垃圾邮件,但是有误判率,所以也有正常的进入垃圾箱了。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明蚁点博客出处!