利用布隆过滤器减少磁盘 IO 或者网络请求,因为一旦一个值必定不存在的话,就可以直接结束查询,比如以下场景:
- 大数据去重;
- 网页爬虫对 URL 的去重,避免爬取相同的 URL 地址;
- 反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱;
- 缓存击穿,将已存在的缓存放到布隆中,当黑客访问不存在的缓存时迅速返回避免缓存及数据库挂掉。
使用GUAVA实现布隆过滤器
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
/**
* Guava版布隆过滤器
*
*/
public class BloomFilterTest {
/**
* @param expectedInsertions 预期插入值
* 这个值的设置相当重要,如果设置的过小很容易导致饱和而导致误报率急剧上升,如果设置的过大,也会对内存造成浪费,所以要根据实际情况来定
* @param fpp 误差率,例如:0.001,表示误差率为0.1%
* @return 返回true,表示可能存在,返回false一定不存在
*/
public static boolean isExist(int expectedInsertions, double fpp) {
// 创建布隆过滤器对象
BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(), 500, 0.01);
// 判断指定元素是否存在
System.out.println(filter.mightContain(10));
// 将元素添加进布隆过滤器
filter.put(10);
// 再判断指定元素是否存在
System.out.println(filter.mightContain(10));
return filter.mightContain(10);
}
public static void main(String[] args) {
boolean exist = isExist(100000000, 0.001);
}
}
public class BloomFilterTest1 {
/**
* 100万
*/
public static final int INSERTIONS = 1000000;
public static void main(String[] args) {
// 初始化一个存储string数据的布隆过滤器,默认fpp(误差率) 0.03
BloomFilter<String> bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), INSERTIONS);
Set<String> set = new HashSet<String>(INSERTIONS);
List<String> list = new ArrayList<String>(INSERTIONS);
for (int i = 0; i < INSERTIONS; i++) {
String uuid = UUID.randomUUID().toString();
bf.put(uuid);
set.add(uuid);
list.add(uuid);
}
/**
* 布隆过滤器误判的次数
*/
int wrong = 0;
/**
* 布隆过滤器正确次数
*/
int right = 0;
int total = 10000;
for (int i = 0; i < total; i++) {
String str = "";
if (i % 100 == 0) {
str = list.get(i / 100);
} else {
str = UUID.randomUUID().toString();
}
/*
String str = i % 100 == 0 ? list.get(i / 100) : UUID.randomUUID().toString();
*/
if (bf.mightContain(str)) {
if (set.contains(str)) {
right++;
} else {
wrong++;
}
}
}
//right 为100
System.out.println("right:" + right);
//因为误差率为3%,所以一万条数据wrong的值在300左右
System.out.println("wrong:" + wrong);
}
}