布隆过滤器(Bloom Filter),是一个很长的二进制向量和一系列随即映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中,它的空间效率和查询时间都远超一般的算法,但是有一定的误识别率和删除困难。

原理

当一个元素被加入到集合中时,通过k个散列函数将这个元素映射称一个位数组的k个点,把他们设置为1。检索时,我们只需要看 这些点是否都是1就知道集合中有没有了;如果这些点中有任何一个0,则被检索元素一定不在;如果都是1,则被检索元素可能存在,这就是布隆过滤器的基本思想。

布隆过滤器跟单哈希函数bit-map不同之处在于,布隆过滤器使用了k个哈希函数,每个字符串跟k个bit对应,从而降低了冲突的概率。

redistemplate布隆过滤器 redis bitmap 布隆过滤器_redis

缓存穿透

一般谈起布隆过滤器,都是通过缓存穿透引起的。
当缓存中没有数据时,不一定代表数据没有缓存,而是有可能数据压根不存在。如果大量请求查询了一个不存在的数据,会对数据库造成很大的性能压力
它和缓存击穿的差别是:缓存穿透是缓存没有起到压力缓冲的作用,而缓存击穿是指缓存失效瞬间大量的请求直接到达数据库
解决缓存穿透有两种方案:

  • 对于不存在的数据,同样记录到缓存中,设置一个特殊值
  • 使用布隆过滤器做前置过滤,可以把所有可能的值保存在布隆过滤器中,从缓存读取数据前先过滤一次:如果布隆过滤器认为值不存在,那么值一定是不存在的,无需查询缓存也无需查询数据库;对于极小概率的误判请求,才会最终让非法 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的大小就能解决同样的问题