首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

大数据去重之Bloom Filter

Bloom Filter是一种高效利用空间的概率数据结构,由Burton HowardBloom于1970年发明,用于检测一个元素是否属于一个集合,但是并不严格要求100%正确的场合。

Bloom-Filter解决的问题

  为了说明Bloom Filter存在的重要意义,举一个实例:

  假设要你写一个网络蜘蛛(web crawler)。由于网络间的链接错综复杂,蜘蛛在网络间爬行很可能会形成“环”。为了避免形成“环”,就需要知道蜘蛛已经访问过那些URL。给一个URL,怎样知道蜘蛛是否已经访问过呢?稍微想想,就会有如下几种方案:

将访问过的URL保存到数据库。

HashSet将访问过的URL保存起来。那只需接近O(1)的代价就可以查到一个URL是否被访问过了。

URL经过MD5或SHA-1等单向哈希后再保存到HashSet或数据库。

Bit-Map方法。建立一个BitSet,将每个URL经过一个哈希函数映射到某一位。

  方法1~3都是将访问过的URL完整保存,方法4则只标记URL的一个映射位

  以上方法在数据量较小的情况下都能完美解决问题,但是当数据量变得非常庞大时问题就来了

  方法1的缺点:数据量变得非常庞大后关系型数据库查询的效率会变得很低。而且每来一个URL就启动一次数据库查询是不是太小题大做了?

  方法2的缺点:太消耗内存。随着URL的增多,占用的内存会越来越多。就算只有1亿个URL,每个URL只算50个字符,就需要5GB内存。

  方法3:由于字符串经过MD5处理后的信息摘要长度只有128Bit,SHA-1处理后也只有160Bit,因此方法3比方法2节省了好几倍的内存。

  方法4:消耗内存是相对较少的,但缺点是单一哈希函数发生冲突的概率太高。若要降低冲突发生的概率到1%,就要将BitSet的长度设置为URL个数的100倍

  实质上上面的算法都忽略了一个重要的隐含条件:允许小概率的出错,不一定要100%准确!也就是说少量url实际上没有没网络蜘蛛访问,而将它们错判为已访问的代价是很小的——大不了少抓几个网页呗。

Bloom-Filter的基本思想

  上述方法4,单个Hash函数高概率的冲突(碰撞)的问题,为了减少冲突,我们可以多引入几个Hash函数,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。

原理要点:一是位数组, 而是k个独立hash函数。

1)位数组:

  假设Bloom Filter使用一个m比特的数组来保存信息,初始状态时,Bloom Filter是一个包含m位的位数组,每一位都置为0,即BF整个数组的元素都设置为0。

image

2)添加元素,k个独立hash函数

  为了表达S=这样一个n个元素的集合,Bloom Filter使用k个相互独立的哈希函数(Hash Function),它们分别将集合中的每个元素映射到的范围中。

  当我们往Bloom Filter中增加任意一个元素x时候,我们使用k个哈希函数得到k个哈希值,然后将数组中对应的比特位设置为1。即第i个哈希函数映射的位置hashi(x)就会被置为1(1≤i≤k)。

  注意,如果一个位置多次被置为1,那么只有第一次会起作用,后面几次将没有任何效果。在下图中,k=2,且有两个哈希函数选中同一个位置(从左边数第五位,即第二个“1“处)。

image

3)判断元素是否存在集合

  在判断y是否属于这个集合时,我们只需要对y使用k个哈希函数得到k个哈希值,如果所有hashi(y)的位置都是1(1≤i≤k),即k个位置都被设置为1了,那么我们就认为y是集合中的元素,否则就认为y不是集合中的元素。下图中y1就不是集合中的元素(因为y1有一处指向了“0”位)。y2可能属于这个集合,并不能100%的肯定该字符串被Bloom Filter记录过的。(因为有可能该字符串的所有位都刚好是被其他字符串所对应)这种将该字符串划分错的情况,称为false positive

image

4)删除元素

  元素加入了就不能被删除了,因为删除会影响到其他字符串。实在需要删除字符串的可以使用Counting bloomfilter(CBF),这是一种基本Bloom Filter的变体,CBF将基本Bloom Filter每一个Bit改为一个计数器,这样就可以实现删除字符串的功能了。

算法优化

通过上面的解释我们可以知道,如果想设计出一个好的布隆过滤器,我们必须遵循以下准则:

好的哈希函数能够尽可能的返回宽范围的哈希值。

位数组的大小(用m表示)非常重要:如果太小,那么所有的位很快就都会被赋值为1,这样就增加了误判的几率。

哈希函数的个数(用k表示)对索引值的均匀分配也很重要。

计算m的公式如下:

这里p为可接受的误判率。

计算k的公式如下:

这里k=哈希函数个数,m=位数组个数,n=待检测元素的个数。

Bloom-Filter的应用

由于存在false positive,会导致不在BF中的值,被误判在其中;所以可以添加一个白名单;当BF返回告诉你这个值存在的时候,需要去storage中查找,如下图:

image

Bloom-Filter的java实现

  BitSet是位操作的对象,值只有0或1(即true 和 false),内部维护一个long数组,初始化只有一个long segement,所以BitSet最小的size是64;随着存储的元素越来越多,BitSet内部会自动扩充,一次扩充64位,最终内部是由N个long segement 来存储;

  默认情况下,BitSet所有位都是0即false;

测试函数:

guava中Bloom-Filter

pom.xml:

用法如下:

指定fpp:

判断是否已经存在

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180303G0NM3300?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券