4

一布隆过滤器简介

布隆过滤器(Bloom Filter)是 1970 年由布隆提出的类似于Set的数据结构。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中,但检索的结果并不是很精确,数据量变大的就会产生误判情形,但布隆过滤器的都是能过滤掉已经存在的内容,所以误判的情况就是不在布隆过滤器中的数据有可能误判为已经存在,这个功能在某些场景下很有用。

布隆过滤器使用场景

布隆过滤器最大的作用就是大数据量下的去重功能,所以经常使用在如下场景

  • 推荐系统,比如商品,新闻推荐,去除已经推荐过的新闻或商品;
  • 网页爬虫对 URL 去重,避免爬取相同的 URL 地址;
  • 大数据情况下,从邮件中判定垃圾邮箱;

二布隆过滤器原理简介

布隆过滤器是由一串很长的二进制向量组成,可以将其看成一个二进制数组;存放0或者1,默认都是0;

添加数据

向布隆过滤器中添加一个元素key时,通过多个hash函数计算多个hash值,并将对应位置的值置为1;由于不同的key就有不同1的位置,用于区分key;但是但数据量大的时候,就会出现相同key被几个hash函数hash后的值一样,所以会出现误差;

布隆过滤器使用contain方法判断元素是否在过滤器中达到去重效果

三实现方式

3.1guava库实现

添加guava依赖

<dependency>  
 <groupId>com.google.guava</groupId>  
 <artifactId>guava</artifactId>  
 <version>29.0-jre</version>  
 </dependency>

实现:

@Test  
public void contextLoads() {  
 // 总数量1W  
 int total = 10000;  
 // 设置过滤器  
 BloomFilter<CharSequence> bf =  
 BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), total);  
 // 初始化 1W条数据到过滤器中  
 for (int i = 0; i < total; i++) {  
 bf.put("知识追寻者" + i);  
 }  
 // 判断值是否存在过滤器中  
 int count = 0;  
 // 插入2w 条数据进行去重校验  
 int addCount = 2*total;  
 for (int i = 0; i < addCount; i++) {  
 if (bf.mightContain("知识追寻者" + i)) {  
 count++;  
 }  
 }  
 System.out.println("匹配数量 " + count);  
}

控制台输出匹配数量大于1w,所以能起到过滤重复数据的作用;

匹配数量 10294

调节fpp误判率可以实现比较精确的过滤

294/2w=0.0147

fpp默认是0.03d, 设置为0.0147d

BloomFilter<CharSequence> bf =  
 BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), total,0.0147d);

匹配结果如下

匹配数量 10143

3.2redis实现方式

redis 4.0 版本才支持布隆过滤器

redis实现方式使用指令 bf.add 添加元素,bf.exists 查询元素是否存在;数据量小的时候看不出来具体差别

127.0.0.1:6379> bf.add codeding h1  
(integer) 1  
127.0.0.1:6379> bf.add codeding h2  
(integer) 1  
127.0.0.1:6379> bf.add codeding h3  
(integer) 1  
127.0.0.1:6379> bf.exists codeding h1  
(integer) 1  
127.0.0.1:6379> bf.exists codeding h2  
(integer) 1  
127.0.0.1:6379> bf.exists codeding h3  
(integer) 1  
127.0.0.1:6379> bf.exists codeding h4  
(integer) 0

使用Redisson实现,版本 3.x以上

<dependency>  
 <groupId>org.redisson</groupId>  
 <artifactId>redisson</artifactId>  
 <version>3.11.4</version>  
</dependency>

如果是springboot

 <dependency>  
 <groupId>org.redisson</groupId>  
 <artifactId>redisson-spring-boot-starter</artifactId>  
 <version>3.10.1</version>  
 </dependency>

实现代码

 @Test  
 public void test2(){  
 Config config = new Config();  
 config.useSingleServer().setAddress("redis://ip:port");  
 config.useSingleServer().setPassword("密码");  
 //构造Redisson  
 RedissonClient redisson = Redisson.create(config);  
 // 获取布隆过滤器  
 RBloomFilter<String> bloomFilter = redisson.getBloomFilter("userList");  
 //初始化布隆过滤器:预计元素为1wL,误差率为3%  
 // 总数量100  
 long total = 100L;  
 bloomFilter.tryInit(total,0.03);  
 // 初始化 1W条数据到过滤器中  
 for (int i = 0; i < total; i++) {  
 bloomFilter.add("zszxz" + i);  
 }  
 int count = 0;  
 // 200 条数据进行去重校验  
 long addCount = 2*total;  
 for (int i = 0; i < addCount; i++) {  
 if (bloomFilter.contains("zszxz" + i)) {  
 count++;  
 }  
 }  
 // 匹配数量 108  
 System.out.println("匹配数量 " + count);  
 }

匹配数量为 108 ,比100多了8条;也可以通过调整误差率实现较为精确的过滤;

关注知识追寻者,获取2020版面试题一份

redis教程


知识追寻者
19 声望0 粉丝