1. 是什么?
-
它是一个二进制
bit
数组,初始为0
-
采用位存储数据结构,节省存储空间
-
1
存在,0
不存在 -
可以添加,但是不能删除
-
存在误判
2. 干什么?
用于快速查找一个集合中是否存在某个元素。尤其是大数据量中快速查找判断是否存在的问题。目的就是“大海捞针”
使用场景:
- 从
10
亿个手机号中如何快速判断10
万个号码是否存在? - 白名单设置,如何正确识别合法用户?
- 黑名单校验垃圾短信
- …
3. 为什么?
因为它主要的作用在于快速查找,我们通过元素查找的过程说明具体的原理和可能存在的问题。
假设,现有一个集合【码,道】,我们查找某元素是否存在?
1、存储逻辑
使用多个不同hash
散列函数对“码”“道”进行计算,对应下标 标记为 1
2、查找逻辑
1、查找示例
例如,在这个集合中,我们查找一个元素“易”。
2、查找步骤
使用多个不同hash
散列函数对“易”进行计算
查找标记位全为 1
代表存在,有一个为 0
代表不存在
如图示,“易”字在最后的位置计算的hash
值对应为0
,判定不存在集合中。
4. 有什么问题?
(1)什么是误判?
因为hash
计算会产生hash
冲突(或者hash
碰撞)的问题。这就意味着:不同的字符可能存在相同的hash
结果值。如下图,“ 有” 和“道” 经过多个hash
计算出相同的值。那么可能判定“有” 也存在于集合中,从而产生误判。
总结:不存在一定不存在,存在不一定存在。
(2)为什么不能删?
布隆过滤器(Bloom Filter
)是一种空间效率极高的概率型数据结构,它允许一定的误报率。但是它不支持删除操作。
主要由于哈希函数可能存在冲突(即不同的元素映射到相同的位置),因此一旦某个位置被设置为1
,它就可能表示多个元素。如果尝试删除一个元素,那么就需要将该元素映射到的所有位置都重置为0
。但是,这样做可能会影响到其他也映射到这些位置的元素,因为无法确定哪些1
是由当前要删除的元素设置的,哪些是由其他元素设置的。
例如,紧跟上边的思路,要是删除“有”,就要将下标【3,7,8
】同时置为 0
。显然误删了 “道”。
(3)有没有办法解决Hash
冲突?
关于这个问题。只能说,减小出现hash
碰撞的概率。而不能彻底杜绝. 对此,有同学肯定想:
-
拉长点:增加
bit
位数
-
更散列:再多用点
hash
函数
事实上,在实际使用中,我们也会根据预估的业务数据,设置总位数和容错率这两个重要参数。这就是怎么用的问题。
5. Go使用布隆过滤器
Go语言里有必要用布隆过滤器嘛?
在Go
语言中是否需要使用布隆过滤器取决于您的具体应用需求。布隆过滤器是一种用于判断一个元素是否属于一个集合的数据结构,它具有高效的查询速度和较小的内存占用,但也有一定的误判率。
因此,使用布隆过滤器通常是基于以下考虑:
-
需要快速查询元素的存在性:如果您的应用程序需要快速确定一个元素是否属于一个集合,而不需要精确的存在性验证,布隆过滤器是一个合适的选择。它的查询速度比在数据库或其他数据结构中搜索要快得多。
-
内存资源有限:如果您希望在内存资源受限的情况下存储大量的元素或数据,布隆过滤器可以节省内存。它的内存占用通常比存储实际元素的数据结构小得多。
-
容忍一定的误判率:布隆过滤器具有一定的误判率,即它可能会将不存在于集合中的元素误报为存在。如果您的应用程序可以容忍一些误报,那么使用布隆过滤器是合理的。
-
需要高性能:布隆过滤器的查询速度非常快,因为它通常只需要几个哈希计算和位操作。这使它非常适合需要高性能的应用。
布隆过滤器在一些应用场景下非常有用,例如缓存缺失优化、重复数据过滤、恶意输入检测等。但它并不适用于所有情况,特别是对于需要绝对准确性的情况。
在使用布隆过滤器时,需要仔细考虑误判率和容忍度,并根据应用的需求来选择合适的配置参数。布隆过滤器通常用于辅助数据结构,而不是替代传统数据库或数据存储。
布隆过滤器通常用于以下场景:
-
缓存缺失优化:当需要查找的数据量很大,而内存有限时,可以使用布隆过滤器来快速判断某个数据是否存在于缓存中,从而减少磁盘或网络访问。
-
重复数据过滤:在某些情况下,需要避免重复数据的插入。布隆过滤器可以用来快速判断一个数据是否已经存在,避免重复。
-
防止恶意输入:在网络应用中,可以使用布隆过滤器来检测恶意输入,例如防止恶意爬虫或垃圾邮件。
-
分布式系统:在分布式系统中,布隆过滤器可以用来判断某个数据是否需要从远程节点获取,从而减少网络开销。
单机版(Golang)
下面是一个简单的示例,演示如何在Go
中使用布隆过滤器。您可以使用第三方库来实现布隆过滤器,如 github.com/wangjia184/sortedset/bf
。
首先,您需要安装这个库:
go get github.com/wangjia184/sortedset/bf
然后,您可以编写代码来创建和使用布隆过滤器:
package main
import (
"fmt"
"github.com/wangjia184/sortedset/bf"
)
func main() {
// 创建一个布隆过滤器,参数分别是预期的元素数量和错误率
filter := bf.New(10000, 0.01)
// 添加元素到布隆过滤器
filter.Add([]byte("example"))
// 检查元素是否存在于布隆过滤器中
if filter.Has([]byte("example")) {
fmt.Println("Element 'example' is in the filter.")
} else {
fmt.Println("Element 'example' is not in the filter.")
}
if filter.Has([]byte("nonexistent")) {
fmt.Println("Element 'nonexistent' is in the filter.")
} else {
fmt.Println("Element 'nonexistent' is not in the filter.")
}
}
布隆过滤器的大小和误判率是可以配置的,根据具体需求进行调整。但需要注意,随着误判率的降低,过滤器所需的内存空间也会增加。
在使用布隆过滤器时,应该考虑到误判率的问题,确保它在实际应用中的误判不会导致严重问题。布隆过滤器对于快速的存在性检查非常有用,但不适用于需要精确匹配的情况。
上面介绍的是单机版的布隆过滤器,但大部分时候我们需要一个分布式场景下的全局布隆过滤器,Go版本的暂时没有找到,可以先看一个Java版的。字节内部有分布式版的布隆过滤器Golang SDK ,但并未开源使用
分布式版(Java)
Java Redisson
中的布隆过滤器就是分布式的。
使用 Redisson
创建布隆过滤器,插入元素,并检查某个元素是否存在。
在 pom.xml
文件中添加 Redisson
依赖:
<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.1</version> <!-- 使用最新版本 -->
</dependency>
</dependencies>
编写代码来创建布隆过滤器,插入元素,并检查元素:
import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
public class RedissonBloomFilterExample {
public static void main(String[] args) {
// 配置 Redisson 客户端
Config config = new Config();
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379"); // 替换为你的 Redis 服务器地址
// 创建 Redisson 客户端实例
RedissonClient redisson = Redisson.create(config);
// 创建布隆过滤器
// 注意:这里的名称 "myBloomFilter" 是布隆过滤器的唯一标识,你可以根据需要更改
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("myBloomFilter");
// 初始化布隆过滤器,设置预期插入的元素数量和误报率
bloomFilter.tryInit(10000L, 0.03);
// 插入元素
bloomFilter.add("example");
bloomFilter.add("example1");
// 查找元素
boolean mightContain = bloomFilter.contains("example");
System.out.println("布隆过滤器中可能包含'example':" + mightContain);
// 关闭 Redisson 客户端
redisson.shutdown();
}
}
注意:
-
由于布隆过滤器的特性,
contains
方法返回true
并不意味着元素一定存在,而返回false
则意味着元素一定不存在。 -
对于
bloomFilter.tryInit(10000L, 0.03)
,的参数设置,应根据实际业务给出。