若是想判断一个元素是否是在一个集合里,通常想到的是将集合中全部元素保存起来,而后经过比较肯定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路,通常来说,计算机中的集合是用哈希表(Hash table)来存储的。html
可是随着集合中元素的增长,咱们须要的存储空间愈来愈大。同时检索速度也愈来愈慢,上述三种结构的检索时间复杂度分别为 O(n),O(logn),O(1)。其中时间复杂度最低的hash table的空间复杂度是O(N),在面对数以亿记的数据时,这种实现方式的存储成本会比较大。面试
好比说,一个像 Yahoo,Hotmail 和 Gmai 那样的公众电子邮件(email)提供商,老是须要过滤来自发送垃圾邮件的人(spamer)的垃圾邮件。一个办法就是记录下那些发垃圾邮件的 email 地址。因为那些发送者不停地在注册新的地址,全世界少说也有几十亿个发垃圾邮件的地址,将他们都存起来则须要大量的网络服务器。若是用哈希表,每存储一亿 个 email 地址, 就须要 1.6GB 的内存(用哈希表实现的具体办法是将每个 email 地址对应成一个八字节的信息指纹(详见:china.googleblog.com/2006/08/blo… ), 而后将这些信息指纹存入哈希表,因为哈希表的存储效率通常只有 50%,所以一个 email 地址须要占用十六个字节。一亿个地址大约要 1.6GB, 即十六亿字节的内存)。所以存贮几十亿个邮件地址可能须要上百 GB 的内存。除非是超级计算机,通常服务器是没法存储的 -- Google黑板报 china.googleblog.com/2007/07/blo…算法
那么有没有其余的方式来实现一样的事呢?本文介绍一下布隆过滤器及其原理和应用。数据库
若是面试官问你,一个网站有 100 亿 url 存在一个黑名单中,每条 url 平均 64 字节。问这个黑名单要怎么存?若此时随便输入一个 url,如何判断该 url 是否在这个黑名单中?数组
布隆过滤器(bloom filter)能够用于检索一个元素是否在一个集合中。它的优势是空间效率和查询时间都远远超过通常的算法,缺点是有必定的误识别率和删除元素困难。缓存
通常的布隆过滤器会提供两个方法:Test 和 Add服务器
Test用来确认某个元素是否在集合内。若是它返回:网络
假正率就是实际数据为false,预测数据(布隆过滤器的判断)为true,的几率。 数据结构
![]()
Add用来添加元素到集合内。并发
这里没有删除,在讲到原理部分时会比较清楚的讲解为何布隆过滤器删除元素困难。
布隆过滤器的原理是,当一个元素被加入集合时,经过K个hash函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,咱们只要看看这些点是否是都是1就(大约)知道集合中有没有它了:若是这些点有任何一个0,则被检元素必定不在;若是都是1,则被检元素极可能在。这就是布隆过滤器的基本思想。
布隆过滤器是一个 bit 向量,长这样:
往布隆过滤器中添加一个元素时,咱们将这个值传入 k 个hash函数,而后将结果位置bit置为1,在图中例子向量或者说数组的长度为50,使用3个hash函数。
当咱们要判断一个元素是否在布隆过滤器中时,咱们把这个值传入k个hash函数中得到映射的k个点。这一次咱们确认下是否全部的点都被置为1了,若是有某一位没有置为1则这个元素确定不在集合中。若是都在那这个元素就有可能在集合中。
看完原理后咱们就知道了为何布隆过滤器中有可能误判以及元素越多假正率(误判概率)越大,由于随着增长的值愈来愈多,被置为 1 的 bit 位也会愈来愈多,这样某个值即便没有被存储过,可是有可能元素的hash函数返回的k个 bit 位都被其余值置位了 1 ,那么程序仍是会判断这个值存在。
同时hash函数的个数也须要权衡,个数越多则布隆过滤器 bit 位置位 1 的速度越快,且布隆过滤器的效率越低;可是若是太少的话,那咱们的误报率会变高。
关于如何选择合适的hash函数个数k和布隆过滤器长度m,有人推导出了公式以下
了解了布隆过滤器的原理后咱们就知道在布隆过滤器中删除元素几乎是不可能的了,不过其实有一种叫作counting bloom filters的数据结构
比较场景的例子是用布隆过滤器减小磁盘IO或者网络请求(都是代价很高的操做)查找不存在的key的时候。
布隆过滤器返回false那么这个值确定不在
由于一旦一个值一定不存在的话,咱们能够不用进行后续昂贵的查询请求。若是它在,那么咱们就去作查找,因为误判率不会过高因此这种代价通常也能承受。