布隆过滤器
问题
认识布隆过滤器
布隆过滤器 (Bloom Filter)是由Burton Howard Bloom于1970年提出,它是一种space efficient的概率型数据结构,用于判断一个元素是否在集合中。在垃圾邮件过滤的黑白名单方法、爬虫(Crawler)的网址判重模块中等等经常被用到。哈希表也能用于判断元素是否在集合中,但是布隆过滤器只需要哈希表的1/8或1/4的空间复杂度就能完成同样的问题。
哈希函数的概念
哈希函数(散列函数)的输入域可以是非常大的范围,比如,任意一个字符串,但是输出域是固定的范围,假设为S,并具有如下性质:
- 典型的哈希函数都有无限的输入值域。
- 当给哈希函数传入相同的输入值时,返回值一样。
- 当给哈希函数传入不同的输入值时,返回值可能一样,也可能不一样,所以会有不同的输入值对应在S中的一个元素上。
- 很多不同的输入值所得到的返回值会均匀地分布在S上(最重要的性质)。
第1~3点性质是哈希函数的基础,第4点性质是评价一个哈希函数优劣的关键,不同输入值所得到的所有返回值越均匀地分布在S上,哈希函数越优秀,并且这种均匀分布与输入值出现的规律无关。比如"al"、"a2"、"aa3"三个输入值比较类似,但经过优秀的哈希函数计算后的结果应该相差非常大。
布隆过滤器的概念
假设有一个长度为m的bit类型的数组,即数组中的每一个位置只占一个bit(每一个bit只有0和1两种状态):
再假设一共有k个哈希函数,这些函数的输出域S都大于或等于m,并且这些哈希函数都足够优秀,彼此之间也完全独立。那么对同一个输入对象(假设是一个字符串记为URL),经过k个哈希函数算出来的结果也是独立的(可能相同,也可能不同),但彼此独立对算出来的每一个结果都对m取余(%m),然后在bit array上把相应的位置设置为1(涂黑)。
我们把bit类型的数组记为 bitMap。接下来按照该方法处理所有的输入对象,每个对象都可能把 bitMap 中的一些白位置涂黑,也可能遇到已经涂黑的位置并让其继续为黑。处理完所有的输入对象后,可能 bitMap 中已经有相当多的位置被涂黑。至此,一个布隆过滤器生成完毕。
检査某一个对象a是否是之前的输入对象时,需要把a通过k个哈希函数算出k个值,然后把k个值取余(%m),就得到在[0,m-1]范围上的k个值。接下来在bitMap上看这些位置是不是都为黑。如果有一个不为黑,说明a一定不在这个集合里。如果都为黑,说明a在这个集合里(可能有误判)。
如果a的确是输入对象,那么在生成布隆过滤器时,bitMap中相应的k个位置一定已经涂黑了,所以在检查阶段,a一定不会被漏过。会产生误判的是a明明不是输入对象,但因为在生成布隆过滤器的阶段输入对象过多,而bitMap过小,就会导致bitMap绝大多数的位置都已经变黑,从而在检查a时错误地认为a是输入对象。
问题分析
黑名单中样本的个数为100亿个,记为n;失误率不能超过0.01%,记为p;每个样本的大小为64B。
一般的哈希函数都可以接收64B的输入对象,不用顾忌单个样本的大小也是使用布隆过滤器的一个好处。
布隆过滤器的大小m由以下公式确定:
根据公式计算出m=19.19n,向上取整为20n,即需要2000亿个bit(25GB)。
哈希函数的个数由以下公式决定:
计算出哈希函数的个数为k=14个。
所以用25GB的bitMap和单独实现14个哈希函数生成布隆过滤器即可。
用如下公式确定布隆过滤器真实的失误率为:
根据这个公式算出真实的失误率为0.006%,这是比0.01%更低的失误率。
哈希函数本身不占用什么空间,最终使用的空间就是bitMap的大小(25GB),基本上服务器的内存都可以达到这个级别。
布隆过滤器会有误报,比如发现“aa5”这个样本不在布隆过滤器中,但是每次计算后的结果都显示其在布隆过滤器中,那么就可以把这个样本加入到白名单中,以后就可以知道这个样本确实不在布隆过滤器中。