使用场景
1.解决缓存穿透。
2.解决高效去除爬虫所抓取页面重复URL。
3.计算网站用户UV。
4.高效判断用户名是否唯一。
5.如何判断垃圾邮件。
核心设计思路:
布隆过滤器(Bloom Filter)的核心实现是一个超大的位数组和几个哈希函数。假设位数组的长度为m,哈希函数的个数为k。当需要判断一个key是否存在时,我们通过对当前key进行k次hash函数计算,如过K次hash计算都命中,说明key存在。当然存在hash冲突的概率,所以可以用在允许小规模误判的场景,当然这个误判的概率我们是可以通过相关代码的实现来降低的,只是需要花费一定的空间,例如以上的开篇的场景都是适用的。
布隆过滤器特点
布隆过滤器一个重要的特点就是不存储实际的key值,在网页大规模爬虫URL去重的情况下,如果我们对每个URL进行存储(一个URL 64长度),然后判断是否重复,我们需要放置到一个Map\Set类似的容器里面。当需要在10亿个URL里面判断是否重复,我们需要的存储空间是非常大的,而布容过滤器很巧妙的通过位数组来实现,一位只需要1bit的存储空间。一个URL如过我们进行k次hash计算,我们最多只要需要k*1bit的存储空间,当然URL多的时候,还会存在部分bit是复用的情况需要耗费的空间还能进一步降低。
使用DEMO
Java语言下有非常多的实现,我们常用的Guava包提供了BloomFilter的实现。
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
public class BloomFilterTest {
private static final int insertions = 1000000; //100w
public static void main(String[] args){
//初始化一个存储string数据的布隆过滤器,初始化大小100w,不能设置为0
BloomFilter<String> bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), insertions,0.00001);
//初始化一个存储string数据的set,初始化大小100w
Set<String> sets = new HashSet<>(insertions);
//初始化一个存储string数据的set,初始化大小100w
List<String> lists = new ArrayList<String>(insertions);
//向三个容器初始化100万个随机并且唯一的字符串---初始化操作
for (int i = 0; i < insertions; i++) {
String uuid = UUID.randomUUID().toString();
bf.put(uuid);
sets.add(uuid);
lists.add(uuid);
}
int wrong = 0;//布隆过滤器错误判断的次数
int right = 0;//布隆过滤器正确判断的次数
for (int i = 0; i < 10000; i++) {
String test = i%100==0?lists.get(i/100):UUID.randomUUID().toString();//按照一定比例选择bf中肯定存在的字符串
if(bf.mightContain(test)){
if(sets.contains(test)){
right ++;
}else{
wrong ++;
}
}
}
System.out.println("=================right====================="+right);//100
System.out.println("=================wrong====================="+wrong);
}
}