介绍Guava Bloom Filter 数据结构**
本文我们探讨来着guava布隆过滤器数据结构。Bloom filter是一种内存有效的概率数据结构,一般用于查找集合中是否包含特定元素。该算法具有假阳性,因此当其返回true时,表示有很大可能性元素在集合中,但不是100%确定。详细内容可以参考《数学之美》中介绍布隆过滤器章节。
maven 依赖
我们使用Guava的布隆过滤器数据结构实现,增加相应依赖,读者可以选择适合的版本:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
算法适用场景
布隆过滤器算法目的是节约空间且速度快。当能接受一定概率的假阳性,确实能在实际应用中节约内存空间。应用场景包括,如新注册邮箱地址是否重复,数据库记录是否重复,因为需要对比的目标数据量很大,传统方式效率低,且占用内存多。
布隆过滤器因为需要占用内存量少,即使非常大的数据量也适合在内存中处理。一些数据库如,Cassandra 和 Oracle在进行实际磁盘或缓存查找之前首先使用该算法进行检查。如查询特定ID请求。如果过滤器返回ID不存在,则数据库不再进行下一步处理,直接诶返回客户端。否则,进一步去磁盘进行查找是否存在。
创建布隆过滤器
假设我们想创建一个上限是500个integer类型,能接受1%(0.01)的假阳性概率。使用Guava库中BloomFilter进行实现。我们需要指定期望插入过滤器的元素数量以及假阳性概率作为参数:
BloomFilter<Integer> filter = BloomFilter.create(
Funnels.integerFunnel(),
500,
0.01);
现在增加一些元素至过滤器中:
filter.put(1);
filter.put(2);
filter.put(3);
我们仅增加三个元素。因为定义最大插入元素量是500,因此过滤器结果应该很准确。下面使用mightContain() 方法进行测试:
assertThat(filter.mightContain(1)).isTrue();
assertThat(filter.mightContain(2)).isTrue();
assertThat(filter.mightContain(3)).isTrue();
assertThat(filter.mightContain(100)).isFalse();
望文生义,当方法返回true时,其不是100%确定给定元素在过滤器中。 mightContain() 方法返回true,准确地描述应该为99%可能性元素在过滤器中,有1%可能性结果为假阳性。但当过滤器返回false时,可能100%确定元素不在过滤器中。
过滤器过度饱和
我们设计布隆过滤器时,我们要为预期元素数量提供一个合理准确值非常重要。否则过滤器返回假阳性概率会大大提高,下面通过过示例说明。
假设我们创建一个过滤器,假阳性概率为1%,期望元素数量为5,但我们插入100000元素:
BloomFilter filter = BloomFilter.create(
Funnels.integerFunnel(),
5,
0.01);
IntStream.range(0, 100_000).forEach(filter::put);
因为期望元素数量很小,然而我们增加了超过限制的元素量,过滤变得过度饱和,产生了比设定概率高的假阳性概率:
ssertThat(filter.mightContain(1)).isTrue();
assertThat(filter.mightContain(2)).isTrue();
assertThat(filter.mightContain(3)).isTrue();
assertThat(filter.mightContain(1_000_000)).isTrue();
我们看到即使1_000_000不在集合中,但仍然返回true。
总结
本文我们探讨了布隆过滤器——基于概率的数据结构,并通过Guava实现进行示例说明。