布隆过滤器(BloomFilter)

目录

 

布隆过滤器的用途

布隆过滤器是什么

比特数组的大小及如何创建

Hash 函数如何设置

布隆过滤器的实际误差计算


布隆过滤器的用途

让我们先来看一个业务场景.

假设某搜索公司的URL黑名单有100亿条记录.业务上需要时刻监控如果用户访问的URL是黑名单中的就驳回访问请求.如何实现这样的黑名单监控功能呢?

最容易的办法当然是将数据存入hash表中,用户访问网页时,查看黑名单hash表中是否包含其正访问的网址以做出正确的回应.伪代码可以简单的写成

if(userUrl == blackList.get(userUrl)){
    reject();
}

虽然hash表加快了URL是否合法的判断时间,但100亿条的URL都存入hash表中,即便每条URL只占用64字节,总共下来最少也要640G内存.一般的服务器是没有这么大内存的.而用一个多台机器的集群来完成这样一件事又得不偿失.

布隆过滤器解决的就是这种场景的问题.它可以让我们只用有限的内存就可以解决原来需要640G内存才能完成的事情,而为此只需要付出微小错误率的代价.布隆过滤器的错误是指将不在黑名单中的URL误判为非法URL而不是将非法的URL判为合法,这在工程上往往是可以接受的.

布隆过滤器是什么

拥有如此强大功能的布隆过滤器,其本质其实是hash函数和字节类型的数组.字节数组中的值只有0和1两种,我们刚好以此来标记URL是否在黑名单中.

假设我们有一个足够长的字节类型的数组来标记100亿个黑名单URL,只需要将黑名单中的URL逐一传入hash函数中,hash函数返回一个int类型的数作为该URL在数组中的index,将该index上的值置为1表示该URL是黑名单中的一个.当用户访问一个URL时,只需要将访问的URL传入hash函数,判断其对应index值是否为1即可.

比特数组的大小及如何创建

基本思路是清楚了,那我们来看一下细节问题.首先,一个足够长的数组是多长呢?我们知道数组的长度就是URL通过hash函数之后的映射域,数组太小会导致hash碰撞的概率增加,合法URL同黑名单URL碰撞就造成了误判,所以数组的长度定义应该根据提前想好的允许误差来进行规划.下面是数组长度的计算公式:

m = -\frac{n * ln p}{(ln 2)^{2}}

公式中,m代表最终数组的大小,n代表样本数,p代表允许误差.计算得到的m如果是小数则向上取整即可.如果100亿URL的允许误差率为0.0001,那么经过计算,最终需要的空间大概是22G多.从最少要640G到只要22G+,这个空间节省是特别可观的.而且现在20多G的内存对于服务器来讲,已经不是高成本的代价了.

如何创建一个长度为m的字节类型数组呢,为了方便计算,假设最终得到的数组大小为100w.我们知道在Java中,一个int类型的数据会占用四个字节,而每个字节又有8个比特,所以一个int数字就是32比特,我们可以创建一个100w/32 = 3125长度的int数组来完成布隆过滤器数据的存储.假设hash函数返回值为903,因为一个int数组代表了32位,我们首先要计算903对应了int数组的第几个index,也就是

903 / 32 -1 = 27

找到了index后需要计算该int值的第几位使我们要标记的.也就是

903 % 27 = 12

创建和使用方法如下:

int [] bloomFileData = new int [31250];
//找到对应index
int index = 903 / 27;
//找到对应位数
int location = 903 % 27;

//标记为1
bloomFilterData[index] = bloomFilterData[index] | (1 << location);

Hash 函数如何设置

设置好m之后,为了能减小hash碰撞对准确率产生的影响,人们又想出了一个办法就是,既然只要是使用了hash函数进行映射就一定存在hash碰撞的情况,那我使用多个hash函数,当一个值过来我多做几次判断,每次都命中才算对应上,这样发生hash碰撞的概率总要小的多了吧.

办法是好办法,可以hash函数的个数也不是越多越好的,极端一点,如果设置100亿个hash函数,那数组中几乎所有的位置都被标记为1了,误判率范围是100%.所以使用多少hash函数也有对应公式计算:

k = \frac{m}{n}ln 2

公式中,m为数组大小,n为样本量.计算出的k如果是小数也向上取整.此处需要说明,k个hash函数必须是独立的,每两个hash函数之间不可以存在相关性.

布隆过滤器的实际误差计算

到此,我们清楚了在明确样本量和期望误判率的情况下,如何来根据这两个已知条件计算数组空间和hash函数个数,但是在根据上边公式计算这两者时,因为数组空间和函数个数不可能有小数,我们都进行了向上取整.此时的误判率不会严格符合我们的期望误判率,如何计算实际误判率呢?公式如下:

(1 - e^{-\frac{kn}{m}})^{k}

k,n,m还是分别代表hash函数的个数,样本量以及数组空间大小.

到此,布隆过滤器就讲完啦.感谢耐心阅读.

猜你喜欢

转载自blog.csdn.net/weixin_39445556/article/details/104973492