海量数据判重——布隆过滤器(Bloom filter)与Bitmap对比

本文对比了布隆过滤器和Bitmap在海量数据判重中的应用。布隆过滤器具有高空间利用率和快速查找速度,但存在假阳性问题。Bitmap则以更小的空间存储数据,但内存需求随最大元素增大。在权衡误判率和空间效率时,可根据实际需求选择合适的数据结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

之前写过一篇Bitmap在海量整数排序中应用的博客,在看过布隆过滤器之后,感觉两个有些相似,但是又有区别,在查阅了很多资料之后,这里决定稍作总结。

关于布隆过滤器(Bloom filter)的介绍部分,大多翻译自Wikipedia

正文

布隆过滤器

简介

布隆过滤器(Bloom filter)是一个高空间利用率的概率性数据结构,由Burton Bloom于1970年提出。被用于测试一个元素是否在集合中(由于集合无重复元素的性质,可用来判重)。

可在数据量大到传统无错误散列(hash)方法需要使用的内存量是不可满足时使用,传统无错散列方法可以消除所有无用的磁盘访问,同时需要使用的内存量也非常大,而布隆过滤器在有限的内存使用量下依旧可以排除大部分无用的磁盘访问。

特性

存在假阳性(将不在集合中的元素误判为在集合中),不存在假阴性(将在集合中的元素误判为不在集合中)。过滤器中的元素个数越多,假阳性的可能性越大,总的来说,当不考虑集合中元素个数的情况下,每个元素由10个以下的bit来表示就可以保证1%以内的假阳性概率。

元素可以被加入过滤器,但不可从过滤器中删除(因为删除的时候有可能会影响到其他元素,之后会细说)。

空间和时间优势

布隆过滤器不需要存储数据项,但是同时它需要在其他地方单独存储真正的数据项。对于一个拥有最优k值且误判率在1%的布隆过滤器,每个元素只需要9.6bits(与元素的大小无关)。这个优点一部分继承自数组的紧凑性,另一方面由它本身的概率性决定。若给每个元素增加4.8bits左右,误判率将会减少十倍。

布隆过滤器在添加和查找元素时,所需要的时间时一个常数,O(k),完全与集合中元素个数无关。没有其他固定空间的集合数据结构有这样的效率,但是对于稀疏散列表来说,平均访问时长在实际使用中比一些布隆过滤器要短。在硬件实现方式中,布隆过滤器的优势在于他的k个查询之间不相关,因此可以并行处理。

算法描述

一个空的布隆过滤器是一串被置为0的bit数组(假设由m位)。同时,应该声明k个不同的散列函数生成一个统一随机分布,每一个散列函数都将元素映射到m个bit中的一个(k是一个小于m的常数,与加入过滤器中的元素个数成比例)。k与相应的m的选择由误判率决定。

向过滤器中添加元素时,通过k个散列函数得到该元素对应的k个位置,并将这些位置置为1.

查询某个元素/测试是否与已有元素重复时,依旧通过k个散列函数得到对应的k个位置,判断这些位置是否为1(若全为1则在集合内/重复)

可以看如下图所示的一个例子,其中,{x,y,z}为集合,w为进行比对的元素,m=18,k=3,不同颜色的箭头表示散列映射关系。可以看出,w并不在{x,y,z}这个集合中。
布隆过滤器示例
注意

①、当k比较大时,设计k个不同且无相关的散列函数是不现实的。对于一些输出的位数较多的优秀的散列函数(优秀指不同bit区间之间联系很小),我们可以将其切割成多个bit区域来代替多个散列函数。或者我们可以传递k个不同的值(例如:0,1,2,…,k-1)到一个散列函数中对其进行初始化;或者将这k个值整合到待计算元素中,再进行计算。对于较大的m,k,无相关性的散列函数可以使误判率的增加量减少。
②、从过滤器中删除元素是不可能的,因为有可能删除的当前元素与其他元素共享了某一个bit。当置该bit为0时,就会产生假阴性,这是绝对不允许的。
③、想要保持误判率低,过滤器的空间使用率(bit数组中置为1的概率)应为50%左右

m值的选取

对于给定的p(误判率)和将要加入集合的元素个数n,m由如下公式定义:

### 基于 Redis Bitmap布隆过滤器方法及原理 #### 方法概述 布隆过滤器Bloom Filter)是一种高效的空间节省型概率数据结构,主要用于快速断某个元素是否属于一个集合。其核心是一个位数组多个哈希函数。通过将输入元素映射到位数组中的特定位置来存储信息。 Redis 提供了一种名为 **Bitmap** 的功能,可以用来模拟布隆过滤器所需的位数组[^1]。具体来说,Redis 中的 `SETBIT` `GETBIT` 命令允许操作二进制位,从而支持构建高效的布隆过滤器实现。 --- #### 实现步骤说明 以下是基于 Redis Bitmap 构建布隆过滤器的核心逻辑: 1. 初始化位数组: 使用 Redis 的键值对机制创建一个新的 Bitmap 键,初始状态为全零。 2. 定义哈希函数集: 需要定义一组独立的哈希函数 \( h_1, h_2, \ldots, h_k \),这些函数会将输入元素映射到位数组的不同索引上[^3]。 3. 插入元素: 对于每一个待插入的元素,计算该元素经过所有哈希函数后的结果,并将其对应的位设置为 1。这可以通过 Redis 的 `SETBIT key offset value` 命令完成。 4. 查询元素: 当查询某元素是否存在时,同样利用相同的哈希函数组计算目标位的位置,并检查这些位是否全部为 1。如果任意一位不为 1,则可断定该元素不存在;否则认为可能存在(存在误的可能性)。 5. 删除元素: 布隆过滤器本身并不支持删除操作,因为单个比特可能对应多个不同元素的结果。但如果确实需要移除某些记录,可通过额外维护反向计数等方式间接解决这一问题。 --- #### 示例代码展示 下面提供一段 Python 脚本演示如何使用 redis-py 库配合 Redis 来实现上述过程: ```python import hashlib import math from redis import StrictRedis class BloomFilter: def __init__(self, capacity, error_rate=0.01, redis_key="bloomfilter", host='localhost', port=6379): self.redis_client = StrictRedis(host=host, port=port, decode_responses=True) self.capacity = capacity self.error_rate = error_rate self.bit_size = int(-capacity * math.log(error_rate) / (math.log(2)**2)) self.hash_count = int((self.bit_size/capacity) * math.log(2)) self.key = redis_key def _hashes(self, item): hashes = [] for seed in range(self.hash_count): md5_hasher = hashlib.md5(f"{seed}{item}".encode('utf-8')) hash_value = int(md5_hasher.hexdigest(), 16) % self.bit_size hashes.append(hash_value) return hashes def add(self, item): positions = self._hashes(item) pipe = self.redis_client.pipeline() for pos in positions: pipe.setbit(self.key, pos, 1) pipe.execute() def check(self, item): positions = self._hashes(item) result = all([int(self.redis_client.getbit(self.key, pos)) for pos in positions]) return result # 测试部分 bf = BloomFilter(capacity=1000, error_rate=0.01) test_items = ["apple", "banana", "cherry"] for i in test_items: bf.add(i) print(bf.check("apple")) # True print(bf.check("orange")) # False ``` 此脚本展示了如何初始化布隆过滤器实例并执行基本的操作——添加新项目以及检测指定项的存在情况。 --- #### 性能特点分析 1. **优点** - 占用内存较小,适合大规模数据场景下的成员关系测试。 - 查找速度极快,时间复杂度接近 O(k),其中 k 是使用的哈希函数数量。 2. **缺点** - 存在一定的假阳性率(False Positive Rate),即可能会错误报告某些未加入过的元素已存在于集合中[^2]。 - 不具备真正的删除能力,除非采用更复杂的变体设计。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值