布隆过滤器(Bloom Filter),是一个很长的二进制向量和一系列随即映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中,它的空间效率和查询时间都远超一般的算法,但是有一定的误识别率和删除困难。
原理
当一个元素被加入到集合中时,通过k个散列函数将这个元素映射称一个位数组的k个点,把他们设置为1。检索时,我们只需要看 这些点是否都是1就知道集合中有没有了;如果这些点中有任何一个0,则被检索元素一定不在;如果都是1,则被检索元素可能存在,这就是布隆过滤器的基本思想。
布隆过滤器跟单哈希函数bit-map不同之处在于,布隆过滤器使用了k个哈希函数,每个字符串跟k个bit对应,从而降低了冲突的概率。
缓存穿透
一般谈起布隆过滤器,都是通过缓存穿透引起的。
当缓存中没有数据时,不一定代表数据没有缓存,而是有可能数据压根不存在。如果大量请求查询了一个不存在的数据,会对数据库造成很大的性能压力
它和缓存击穿的差别是:缓存穿透是缓存没有起到压力缓冲的作用,而缓存击穿是指缓存失效瞬间大量的请求直接到达数据库
解决缓存穿透有两种方案:
- 对于不存在的数据,同样记录到缓存中,设置一个特殊值
- 使用布隆过滤器做前置过滤,可以把所有可能的值保存在布隆过滤器中,从缓存读取数据前先过滤一次:如果布隆过滤器认为值不存在,那么值一定是不存在的,无需查询缓存也无需查询数据库;对于极小概率的误判请求,才会最终让非法 Key 的请求走到缓存或数据库。
缺点
布隆过滤器之所以能做到在时间和空间效率比较高,是因为牺牲了判断的准确率、删除的遍历性
- 存在误判,可能要查到的元素并没有在容器中,但是hash之后得到的k个位置上都是1。如果布隆过滤器存储的时黑名单,那么可以通过建立一个白名单来存储可能会误判的元素。
- 删除困难。一个放入容器的元素映射到bit数组的k个位置上是1,删除的时候不能简单的直接置为0,因为会影响其它元素的判断。
实现
在使用布隆过滤器时,需要提供预估数据量n和期望的误判率fpp
在实现布隆过滤器时,需要进行哈希函数的选取以及bit数组的大小
- bit数组大小的选择:
m=-(nlnfpp)/(ln2)^2 - 哈希函数的选择:
由预估数据量n以及bit数组长度m,可以得到一个hash函数的个数k
k=(m/n)ln2
哈希函数的选择对性能的影响时很大的,一个好的哈希函数要能近似等概率的将字符串映射到各个bit,选择k个不同的哈希函数比较麻烦,一种简单的方法时选择一个哈希函数,然后送入k个不同的参数。
错误率越大,所需空间和时间越小,错误率越小,所需的空间和时间越大
应用场景
- cerberus在收集监控数据的时候,有时候系统的监控项量很大,需要检查一个监控项名称是否已经记录到db,如果没有的话需要写入db
- 爬虫过滤已经抓取过的url
- 垃圾邮件过滤。如果使用hash表,每存储一亿个Email地址,需要1.6GB内存,存储几十亿邮件地址可能需要上百G的内存。而布隆过滤器只需要hash表1/8到1/4的大小就能解决同样的问题