首页 电商直播

海量数据去重利器:位图与布隆过滤器原理、实践与避坑

分类:电商直播
字数: (9543)
阅读: (8658)
内容摘要:海量数据去重利器:位图与布隆过滤器原理、实践与避坑,

在互联网应用中,数据去重是一个常见需求。例如,在爬虫系统中,我们需要避免重复抓取相同的网页;在推荐系统中,我们需要过滤掉用户已经看过的商品。当数据量较小时,我们可以直接使用哈希表(例如 Java 中的 HashMap,Python 中的 dict)来解决。但是,当数据量达到亿级别甚至更大时,哈希表的内存占用会变得非常可观,甚至超出服务器的承受能力。这个时候,我们就需要考虑使用更节省内存的数据结构,例如位图布隆过滤器。传统的哈希算法在解决海量数据问题时,面临着内存占用的巨大挑战。

位图(Bitmap):极致的空间压缩

原理

位图(Bitmap),顾名思义,就是用一个 bit 位来表示一个元素是否存在。例如,我们要表示 0 到 7 这 8 个数字,只需要 8 个 bit 位,也就是 1 个字节。如果数字存在,则将对应的 bit 位设置为 1,否则设置为 0。

假设我们要表示集合 {1, 3, 5},对应的位图如下:

下标: 0 1 2 3 4 5 6 7
位图: 0 1 0 1 0 1 0 0

代码示例(Java)

import java.util.BitSet;

public class BitmapExample {
    public static void main(String[] args) {
        BitSet bitSet = new BitSet(8); // 初始化位图,长度为 8

        bitSet.set(1); // 设置下标为 1 的 bit 为 1
        bitSet.set(3); // 设置下标为 3 的 bit 为 1
        bitSet.set(5); // 设置下标为 5 的 bit 为 1

        System.out.println(bitSet.get(1)); // 输出 true
        System.out.println(bitSet.get(2)); // 输出 false
    }
}

适用场景与局限性

位图适用于数据范围已知且较小的情况。例如,我们要统计 1 亿个用户的 ID 是否在 1 到 10 亿的范围内,可以使用位图来解决。但是,如果数据范围过大,例如用户的 ID 是 64 位的整数,那么位图的内存占用也会变得非常巨大。

海量数据去重利器:位图与布隆过滤器原理、实践与避坑

此外,位图只能判断元素是否存在,不能存储其他信息。

布隆过滤器(Bloom Filter):概率型数据结构

原理

布隆过滤器是一种概率型数据结构,用于判断一个元素是否可能存在于一个集合中。与位图不同的是,布隆过滤器允许一定的误判率,也就是说,它可能会将不存在的元素误判为存在,但是不会将存在的元素误判为不存在。

布隆过滤器由一个 bit 数组和 k 个哈希函数组成。当要将一个元素加入集合时,我们使用 k 个哈希函数分别计算出 k 个哈希值,然后将 bit 数组中对应的 k 个位置设置为 1。当要判断一个元素是否在集合中时,我们同样使用 k 个哈希函数计算出 k 个哈希值,然后检查 bit 数组中对应的 k 个位置是否都为 1。如果都为 1,则认为该元素可能存在于集合中;否则,认为该元素一定不存在于集合中。

海量数据去重利器:位图与布隆过滤器原理、实践与避坑

代码示例(Guava)

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

public class BloomFilterExample {
    public static void main(String[] args) {
        BloomFilter<Integer> bloomFilter = BloomFilter.create(
                Funnels.integerFunnel(),
                1000, // 期望插入的数据量
                0.01 // 期望的误判率
        );

        bloomFilter.put(1); // 将元素 1 加入布隆过滤器
        bloomFilter.put(2); // 将元素 2 加入布隆过滤器

        System.out.println(bloomFilter.mightContain(1)); // 输出 true
        System.out.println(bloomFilter.mightContain(3)); // 输出 false (可能误判为 true)
    }
}

误判率与哈希函数

布隆过滤器的误判率受到 bit 数组的大小和哈希函数的个数的影响。bit 数组越大,哈希函数个数越多,误判率越低。但是,bit 数组越大,内存占用越高;哈希函数个数越多,计算量越大。

选择合适的哈希函数也很重要。哈希函数应该尽可能地减少冲突,并且应该尽可能地分散。

适用场景与局限性

布隆过滤器适用于允许一定误判率的场景。例如,在缓存系统中,我们可以使用布隆过滤器来判断一个 key 是否存在于缓存中。如果布隆过滤器认为 key 不存在,那么我们就可以直接返回,而不需要去查询数据库。这样可以减少数据库的压力。常见的应用场景包括:

海量数据去重利器:位图与布隆过滤器原理、实践与避坑
  • 网页 URL 去重:防止重复爬取相同的网页。
  • 垃圾邮件过滤:判断邮件是否为垃圾邮件。
  • 缓存穿透:防止恶意请求穿透缓存,直接访问数据库。

布隆过滤器的局限性在于它存在误判率,并且不能删除元素。因为删除元素可能会影响其他元素的判断。

实战避坑:参数调优与监控

在使用布隆过滤器时,需要根据实际情况选择合适的参数,包括 bit 数组的大小和哈希函数的个数。如果参数选择不当,可能会导致误判率过高,或者内存占用过大。

此外,还需要对布隆过滤器的性能进行监控。例如,可以监控布隆过滤器的误判率,以及布隆过滤器的内存占用。

海量数据去重利器:位图与布隆过滤器原理、实践与避坑

在 Nginx 中,虽然没有直接内置布隆过滤器的模块,但是可以通过 Lua 脚本来实现类似的功能,用于缓解高并发下的缓存穿透问题。结合 OpenResty 提供的 ngx.shared.DICT 共享内存,可以实现一个简单的布隆过滤器,并根据实际并发连接数和后端服务器的负载情况动态调整参数,以达到最佳性能。

在实际应用中,可以借助宝塔面板等工具来监控服务器的 CPU、内存、网络等资源的使用情况,并根据监控结果对布隆过滤器的参数进行优化。

数据结构选型总结

数据结构优点缺点适用场景
位图空间占用小,速度快只能表示存在与否,数据范围需要已知且较小数据范围已知且较小,需要快速判断元素是否存在的情况
布隆过滤器空间占用小,速度快,允许一定的误判率存在误判率,不能删除元素允许一定误判率,需要快速判断元素是否存在的情况,例如缓存穿透等

海量数据去重利器:位图与布隆过滤器原理、实践与避坑

转载请注明出处: 代码一只喵

本文的链接地址: http://m.acea1.store/blog/234672.SHTML

本文最后 发布于2026-04-16 05:37:45,已经过了11天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • e人代表 2 天前
    代码示例很实用,直接就能拿来用,赞一个!
  • 小明同学 6 天前
    请问作者,如果需要删除布隆过滤器中的元素,有没有什么替代方案或者改进方法?
  • 蛋炒饭 4 天前
    请问作者,如果需要删除布隆过滤器中的元素,有没有什么替代方案或者改进方法?