布隆过滤器的由来可以追溯到1970年代,由一个名叫Burton Howard Bloom的美国计算机科学家提出。他在1970年的一篇论文中首次描述了这个概念,并将其称为"Bloom filter"。
Bloom当时的目标是设计一种高效的数据结构,用于在大规模数据库中进行快速查询。他的主要思想是通过使用一个位数组和多个哈希函数,能够在不存储实际数据的情况下,快速判断一个元素是否存在于集合中。这种数据结构的提出,为解决大规模数据的查找问题提供了一种经济高效的方法。
布隆过滤器最初的应用场景是在数据库系统中,用于加速查询操作。它可以有效地减少磁盘I/O操作,提高查询效率,并节省存储空间。后来,布隆过滤器得到了广泛的应用,在各种领域都发挥了重要作用。
随着互联网的迅速发展和大数据时代的到来,布隆过滤器的应用范围越来越广泛。它被用于网络缓存、分布式系统、垃圾邮件过滤、URL去重、爬虫的URL判重、密码学等领域。由于其高效的查询性能和低存储成本,布隆过滤器成为了解决大规模数据处理和高并发访问的重要工具。
总结来说,布隆过滤器是由美国计算机科学家Burton Howard Bloom提出的一种高效的概率型数据结构,用于快速判断一个元素是否存在于集合中。它的提出解决了大规模数据查询的问题,并在各个领域得到了广泛的应用和发展。
布隆过滤器其实就是一个很长的二进制
向量和一系列随机映射函数,主要用来判断一个元素是否存在一个集合中,存储的数据不是0就是1,默认是0
1代表存在某个数据,0代表不存在
因为布隆过滤器就是一个二进制数据的集合,当有一个数据加入到这个集合的时候,会经过下面的步骤
将k个下标对应的二进制数据改为1
布隆过滤器主要就是来查询一个数据在不在这个二进制集合中,具体方法如下
布隆过滤器具有以下几个主要的优点:
综上所述,布隆过滤器具有高效的查询性能、较低的存储需求、支持高并发访问、可扩展性强和简单高效的实现等优点,使其成为解决大规模数据查询和去重等问题的有效工具。
布隆过滤器虽然有很多优点,但也存在一些缺点,主要包括以下几个方面:
误判率
(False Positive):由于布隆过滤器的设计特性,它可能会产生一定的误判率,即认为一个元素存在于集合中,但实际上并不存在。这是因为多个元素经过哈希函数映射后可能会映射到相同的位数组位置上,从而导致冲突。误判率随着位数组的大小和哈希函数的数量增加而降低,但无法完全消除。不支持删除操作
:布隆过滤器的位数组中存储的是元素是否存在的信息,而不存储元素本身。因此,在布隆过滤器中删除一个元素是比较困难的,通常需要重新创建一个新的布隆过滤器。这限制了布隆过滤器在某些场景下的灵活性和可用性。综上所述,布隆过滤器的主要缺点包括误判率、不支持删除操作、无法获取具体元素、存储空间浪费以及初始化时的时间和计算开销。在使用布隆过滤器时,需要根据实际需求和场景权衡其优缺点,选择合适的数据结构。
假如“你好”和“hello”这2个数据的hash值一样,那么他们将会存储到同一位置
当我们要删除“hello”这个数据的时候,我们会删除存储“hello”这个数据的hash值的位置,由于“hello”和“你好”存储在同一位置,这样子会造成误删
同理
如果仅仅存储了hello,没有存储“你好”,当我们查询“你好”的时候,由于 假设“你好”和“hello”这2个数据的hash值一样,会造成误判
引入依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
编写核心代码,我们使用下面的代码来测试一下布隆过滤器的误判率
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
public class BloomFilterCase {
/**
* 预计要插入多少数据
*/
private static int size = 1000000;
/**
* 期望的误判率
*/
private static double fpp = 0.01;
/**
* 布隆过滤器
*/
private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, fpp);
public static void main(String[] args) {
// 插入10万样本数据
for (int i = 0; i < size; i++) {
bloomFilter.put(i);
}
// 用另外十万测试数据,测试误判率
int count = 0;
for (int i = size; i < size + 100000; i++) {
if (bloomFilter.mightContain(i)) {
count++;
System.out.println(i + "误判了");
}
}
System.out.println("总共的误判数:" + count);
}
}
这里的误判率可以通过fpp参数进行调节,
fpp越小,需要的内存空间就越大
:0.01需要900多万位数,0.03需要700多万位数。
fpp越小,集合添加数据时,就需要更多的hash函数运算更多的hash值,去存储到对应的数组下标里。
上面使用Guava实现的布隆过滤器是把数据放在了本地内存中。分布式的场景中就不合适了,无法共享内存。
我们还可以用Redis来实现布隆过滤器,这里使用Redis封装好的客户端工具Redisson。
引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.4</version>
</dependency>
编写核心代码
public class RedissonBloomFilter {
public static void main(String[] args) {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
config.useSingleServer().setPassword("1234");
//构造Redisson
RedissonClient redisson = Redisson.create(config);
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("phoneList");
//初始化布隆过滤器:预计元素为100000000L,误差率为3%
bloomFilter.tryInit(100000000L,0.03);
//将号码10086插入到布隆过滤器中
bloomFilter.add("10086");
//判断下面号码是否在布隆过滤器中
//输出false
System.out.println(bloomFilter.contains("123456"));
//输出true
System.out.println(bloomFilter.contains("10086"));
}
}
代码参考文章:https://mp.weixin.qq.com/s/vwy5oGEWNuQ7k5DQfGrhCQ
在技术的道路上,我们不断探索、不断前行,不断面对挑战、不断突破自我。科技的发展改变着世界,而我们作为技术人员,也在这个过程中书写着自己的篇章。让我们携手并进,共同努力,开创美好的未来!愿我们在科技的征途上不断奋进,创造出更加美好、更加智能的明天!