1.布隆过滤器
1.1.简介
本质是一种数据结构,本质是一个很长的二进制向量和一系列随机映射函数,可以简单理解成一个二进制数组。二进制的特点:不是0就是1。充分利用了数组的快速查询功能
主要用于判断一个元素是否在一个集合中,0代表不存在,1代表存在,有一点的误判率
1.2.场景(解决大规模数据下不需要精确过滤的场景)
- 检查垃圾邮件地址
- 爬虫URL地址去重
- redis缓存穿透问题(需要预先把需要查询的过滤条件放入布隆过滤器,先查布隆过滤器,如果没有则返回没有,如果有,查redis缓存,缓存没有,查库写缓存),在生产中一般用redission框架去
1.3.执行过程
- 通过K个哈希函数计算该数据,返回K个计算出的hash值
- 将计算出的hash值对应数据组的下标,并设置该下标的位置为1
- 在判断该元素是否存在?(当K个哈希值对应的数组位置全为1的时候,则代表该元素存在,否则,该元素不存在)
为什么会存在误判率问题?
因为两个不同的数据经过hash函数运算得到的结果可能是相同的。
1.4.优缺点
优点:优点很明显,二进制组成的数组,占用内存极少,并且插入和查询速度都足够快。
缺点:随着数据的增加,误判率会增加;还有无法判断数据一定存在;另外还有一个重要缺点,无法删除数据。
2.测试布隆过滤器
引入谷歌开源工具库
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
main方法代码如下:
//布隆过滤器的大小
public static int expectedInsertions = 10000;
//预测的误判率
public static double fpp = 0.01;
public static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), expectedInsertions, fpp);
public static void main(String[] args) {
for (int i=0;i < expectedInsertions;i++) {
bloomFilter.put(i);
}
List<Integer> lists = Lists.newArrayList();
for (int i = expectedInsertions + 100000; i < expectedInsertions + 200000; i++) {
if (bloomFilter.mightContain(i)) {
lists.add(i);
}
}
System.out.println("误判数:" + lists.size());
System.out.println("误判率:" + new BigDecimal(lists.size()).divide(new BigDecimal(expectedInsertions)).doubleValue());
}
debug执行过程,看两个参数bigCount和numHashFunctions,一个是所占内存的大小,一个是hash函数的个数
如果修改cpp成0.00001,再次debug一下
对比可以发现,误判率改小或者数据量增加,就会增加hash函数的个数,增加程序的运行时间,增大内存空间