布隆过滤器
在1970年的时候,有一个叫做布隆的前辈对于判断海量元素中元素是否存在的问题进行了研究,也就是到底需要多大的位图容量和多少个哈希函数,它发表了一篇论文,提出的这个容器就叫做布隆过滤器。
工作原理
首先,布隆过滤器的本质就是一个位数组和若干个哈希函数。
集合里面有3个元素,要把它存到布隆过滤器里面去,应该怎么做?首先是a元素,这里我们用3次计算。b.c元素也一样。
元素已经存进去之后,现在我要来判断一个元素在这个容器里面是否存在,就要使用同样的三个函数进行计算。
比如d元素,我用第一个函数f1计算,发现这个位置上是1,没问题。第二个位置也是1,第三个位置也是1。
如果经过三次计算得到的下标位置值都是1,这种情况下,能不能确定d元素一定在这个容器里面呢?实际上是不能的。比如这张图里面,这三个位置分别是把a,b,c存进去的时候置成1的,所以即使d元素之前没有存进去,也会得到三个1,判断返回true。
所以,这个是布隆过滤器的一个很重要的特性,因为哈希碰撞不可避免,所以它会存在一定的误判率。这种把本来不存在布隆过滤器中的元素误判为存在的情况,我们把它叫做假阳性(False Positive Probability,FPP)。
我们再来看另一个元素,e元素。我们要判断它在容器里面是否存在,一样地要用这三个函数去计算。第一个位置是1,第二个位置是1,第三个位置是0。
e元素是不是一定不在这个容器里面呢?可以确定一定不存在。如果说当时已经把e元素存到布隆过滤器里面去了,那么这三个位置肯定都是1,不可能出现0。
总结:布隆过滤器的特点:
从容器的角度来说
- 如果布隆过滤器判断元素在集合中存在,不一定存在
- 如果布隆过滤器判断不存在,一定不存在电包学
从元素的角度来说
- 如果元素实际存在,布隆过滤器一定判断存在
- 如果元素实际不存在,布隆过滤器可能判断存在
Guava的实现
谷歌的Guava里面就提供了一个现成的布隆过滤器。
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
<dependency>
创建布隆过滤器:
BloomFilter<String> bf -BloomFilter. create(Funnels.stringFunnel(Charsets. UTF_8),insertions);
布隆过滤器提供的存放元素的方法是put()。
布隆过滤器提供的判断元素是否存在的方法是mightContain()。
if(bf.mightContain(data)){
if(sets.contains(data)){
//判断存在实际存在的时候,命中
right++;
continue;
}
//判断存在却不存在的时候,错误
wrongt++;
}
布隆过滤器把误判率默认设置为0.03,也可以在创建的时候指定。
public static <T> BloomFilter<T> create(Funnel<? super T> funnel, long expectedInsertions) {
return create(funnel, expectedInsertions, 0.03D);
}
位图的容量是基于元素个数和误判率计算出来的。
long numBits = optimalNumOfBits(expectedInsertions, fpp);
根据位数组的大小,我们进一步计算出了哈希函数的个数。
int numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits);
存储100万个元素只占用了0.87M的内存,生成了5个哈希函数。
布隆过滤器在项目中的使用
布隆过滤器的工作位置:
因为要判断数据库的值是否存在,所以第一步是加载数据库所有的数据。在去Redis查询之前,先在布隆过滤器查询,如果bf说没有,那数据库肯定没有,也不用去查了。
如果bf说有,才走之前的流程。
/**
* 布隆过滤器并发测试,在redis和数据库之间
*/