海量数据下使用bitmap和布隆过滤器去重

海量数据下使用bitmap和布隆过滤器去重

  • 常规情况下的hash表去重


    对于常规的少量数据去重,我们往往采用hash表来去重。这种方法比较简单,就是计算出hash值进行比较,然后hash值相同的在进行下额外的判断来确认是否是同一个数据即可。

  • bitmap去重


    在海量数据进行去重的条件下,如果继续使用hash表则会出现一个很严重的问题,由于hash表是建立了一个hash值与实际数据的意义对应关系,对于十万乃至百万、千万的数据则完全是不现实的。首先对于去重的问题,我们只需要标记该数据有没有出现过,并不需要去存储该数据。从这方面入手,便有了bitmap。
    这里我们假设待处理的数据是int型数据,占32位。则数值区间为2^32,一共需要2^32位来标识所有的数值,那么我们只需要含有2^29个byte的byte数组来表示即可。使byte[0]表示最低的8位,依次类推,按顺序排列。数据13则对应byte[1]中第5位设置为1,8+5=13,表示13出现过。
    这里编程实现时一般采用位运算进行。例如
    设置该数值出现过:

    public void setFlag(int num) {
    flags[num >> 3] = flags[num >> 3]|(0x01 << (num & (0x07)));
    }

    首先num>>3表示将num左移三位,等价于num/8,这是应为每一个byte占8个字节相当于标记0-7。8/8=1则其标记位在byte[1]中。num & (0x07)则相当于num%8,高位与上0111后为都为0,只有最后三位保留下来。这样值域就是0-7。
    查询制定数据是否出现过:

    public static boolean getFlag(int num) {
        return flags[num >> 3] >> (num & (0x07)) & 0x01;
    }

    显然bitmap的大小不会随数据量的变化而变化,他只受原始数据分布区间的影响,所以非常适合来处理海量数据。但是如果数据量比较小的话,它的效率可能就稍逊于hash表了。

  • 布隆过滤器去重


    bitmap虽然好用,但是对于很多实际情况下的大数据处理它还是远远不够的,例如如果我们要进行64bit的long型数据去重,那我们需要含有2^61个byte的byte数组来存储,这显然是不现实的。那我们如何来优化呢,很明显加入我们申请了这么打的byte数组来标记数据,可想而知其空间利用率是极地的。布隆过滤器正是通过提高空间利用率来进行标记的。
    如下图所示
    这里写图片描述
    它采用k个不同的散列函数来进行计算,将数值散列到k个不同的位置,然后将这些位置置为1.上图为例,k=3对于数值w他所对应的三个数值不全为1,所以判定其不在集合中。想到这里大家应该意识到了一个问题,如果一个数值v进行散列计算后位置落在3,4,5(从0开始计算),那么将判定该数据在当前集合中,这就产生了误判。会将不在集合中的数据误认为存在集合中,但是判定不在集合中的情况那将一定不在集合中。我们将这种错误判断的概率叫做误判率。接下来我们计算一下误判率:
    假设目前有m个bit,k个散列函数,集合中存在n个数据。
    当k=1时,插入一个元素后某个位置为0的概率为

    11m 1 − 1 m

    插入n个后某个位置还为0的概率为
    11mn ( 1 − 1 m ) n

    每次插入元素时有k个散列函数,相当于把k个位置置为1。所以k个散列函数插入n个数据后某一个位置为0的概率为
    11mnk ( 1 − 1 m ) n k

    为1的概率则为
    111mnk 1 − ( 1 − 1 m ) n k

    所以当插入n个元素后,查找某个元素是否在集合中事。k个散列位置全为1的概率为(产生误判,比真实值要大,应为这个数可能真的在集合中)
    111mnkk ( 1 − ( 1 − 1 m ) n k ) k

    具体往下的推导大家可以自行百度,看看paper。这里只是简单介绍一下。

### 基于 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]。 - 不具备真正的删除能力,除非采用更复杂的变体设计。 ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值