暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

众里寻他千百度:Bloom Filter

Onebyte 2021-04-06
401
01

什么是布隆过滤器


隆过滤器(Bloom Filter)是1970年由布隆提出的。

它实际上是一个很长的二进制向量一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间越来越大。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为 O(n),O(log n),O(1)。

因此,逐渐演化出布隆过滤器

02

布隆过滤器的原理

Bloom Filter的核心:一个固定大小的二进制位向量(位数组)和一组hash函数。

上图中,数据集是S{x,y,z},每个hash函数以一条有向线段表示。这里我们约定二进制数组的长度为m,且此二进制数组的初始值均为0;hash函数的数目为k。

当把数据元素“插入”过滤器时:

通过 k个散列函数将这个数据元素映射成一个位数组中的 k个槽位,并把它们置为 1。图中所示是3个数据元素(x、y、z)各自被3个hash函数映射到3个槽位。

检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:

  • 如果这些点有任何一个0,则被检元素一定不在;如上图中的n数据元素;

  • 如果都是1,则被检元素很可能在,如m数据元素。

为什么说是可能存在,而不是一定存在呢?那是因为映射函数本身就是散列函数,散列函数是会有碰撞的。
03

误判率及其分析

由于哈希冲突的存在,不同的数据元素在经过哈希映射后,存在映射到相同bit槽位概率。这样就无法区分究竟是哪个输入产生的。

这种情况也造成了布隆过滤器的删除问题,因为布隆过滤器的每一个bit槽位并不是独占的,很有可能多个元素共享了某一位。如果我们直接删除这一位的话,会影响其他的元素。

既然无法避免其误判率,那么如何降低其误判率呢?我们知道,在构建布隆过滤器时,有几个相关变量:

  • 位数组(Bit Array)长度:m

  • 哈希函数(Hash Function)个数:k

  • 已添加元素数量(Items in the filter):n

  • 误判率(False positive probability):fpp
在固定误判率的情况下,可以得到位数组长度m和哈希函数个数k的最优配置。

误判率(False positive probability)

位数组(Bit Array)长度m:

哈希函数(Hash Function)个数k:

一般来说,在满足业务的场景下,可以选择合适的误判率,进而由上述公式推算出m和k的值。幸运的是,在实际开发中,一些开源封装已经帮助我们实现,我们只需要关注对业务数据量和误判率的预估。
从debug来看,越是低的误判率,位数组(Bit Array)长度越长,所占用的内存空间越大,在实际使用时候,需结合实际选择合适的误判率。

上述推导可以参见:https://en.wikipedia.org/wiki/Bloom_filter

04

优缺点

优点:

相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。

  • 布隆过滤器存储空间和插入/查询时间复杂度都是常数O(k);

  • 散列函数相互之间没有关系,方便由硬件并行实现;

  • 布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势;

  • 布隆过滤器可以表示全集,其它任何数据结构都不能。

缺点:

但是布隆过滤器的缺点和优点一样明显。

  • 误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣;

  • 一般情况下不能从布隆过滤器中删除元素。我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加1, 这样删除元素时将计数器减掉就可以了。然而要保证安全地删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面,这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。

在降低误算率方面,有不少工作,使得出现了很多布隆过滤器的变种,如为了解决布隆过滤器不能删除元素的问题而生的布谷鸟过滤器。
05

布隆过滤器的使用场景

数据库类:

Google Bigtable,Apache HBase和Apache Cassandra以及Postgresql 使用布隆过滤器来减少对不存在的行或列的磁盘查找;

缓存类:

Redis使用布隆过滤器防止缓存穿透;

去重类:

Medium使用布隆过滤器避免推荐给用户已经读过的文章;

网页爬虫对URL去重,避免爬取相同的URL地址;

安全类(黑名单):

反垃圾邮件,从数十亿个垃圾邮件列表中判断某个邮箱是否是垃圾邮箱;

Chrome使用布隆过滤器识别恶意URL。

上述使用场景源自B站凯特Kate_zts的总结。

06

Bloom Filter开源实现

常见的开源实现有如下几种:

  • Rebloom - Bloom Filter Module for Redis (注:redis Module在redis4.0引入)
  • 谷歌的guava中的Bloom Filter
  • Redisson中的Bloom Filter实现

笔者基于guava的Bloom Filter写了测试误判率的测试代码,详见:

https://github.com/SmilingBug/bloom-filter-sample
07

引用

[1] https://en.wikipedia.org/wiki/Bloom_filter

[2] https://crossoverjie.top/2018/11/26/guava/guava-bloom-filter/

[3] https://github.com/AobingJava/JavaFamily/blob/master/docs/redis/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8(BloomFilter).md

[4] https://xie.infoq.cn/article/db1fdba6462ed05c8adbd73ea

[5] https://brilliant.org/wiki/bloom-filter/

[6] https://www.jianshu.com/p/bef2ec1c361f

[7] https://github.com/google/guava/wiki/HashingExplained#bloomfilter

[8] http://llimllib.github.io/bloomfilter-tutorial/

[9] https://juejin.cn/post/6844903801908887566


点个

在看

你最好看

文章转载自Onebyte,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论