news 2026/6/10 14:57:54

数据结构:布隆过滤器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数据结构:布隆过滤器

数据结构:布隆过滤器

布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,由霍华德·布隆在1970年提出,用于快速判断一个元素是否存在于一个集合中。它的核心特点是存在误判的可能,但不存在漏判,即:

  • 若判断元素“不存在”,则一定不存在;
  • 若判断元素“存在”,则可能存在(存在一定的误判率);
  • 支持高效的插入查询操作,且不支持删除元素(普通布隆过滤器)。

资料:https://pan.quark.cn/s/43d906ddfa1bhttps://pan.quark.cn/s/90ad8fba8347https://pan.quark.cn/s/d9d72152d3cf

一、布隆过滤器的核心原理

布隆过滤器的底层依赖二进制位数组多个独立的哈希函数,核心流程如下:

1. 初始化

  • 申请一个长度为m的二进制位数组(初始值全为0);
  • 选择k个独立的哈希函数h₁, h₂, ..., h_k,每个哈希函数能将输入元素映射到[0, m-1]范围内的一个整数索引。

2. 插入元素

对于要插入的元素x

  1. 依次通过k个哈希函数计算,得到k个索引值h₁(x), h₂(x), ..., h_k(x)
  2. 将二进制位数组中这k个索引对应的位置置为1

3. 查询元素

对于要查询的元素y

  1. 依次通过k个哈希函数计算,得到k个索引值h₁(y), h₂(y), ..., h_k(y)
  2. 检查位数组中这k个索引对应的位置:
    • 所有位置都是1→ 判定元素y可能存在(存在误判);
    • 任意一个位置是0→ 判定元素y一定不存在

核心特性解释

  • 误判原因:不同元素通过哈希函数计算后,可能会映射到相同的索引位置,导致“哈希冲突”。当一个不存在的元素的k个哈希索引恰好都被其他元素置为1时,就会误判为“存在”。
  • 无漏判原因:只有元素确实插入过,其k个哈希索引才会全部置1;若有一个位置为0,则元素一定没插入过。

二、关键参数与误判率计算

布隆过滤器的性能由三个核心参数决定:

  1. 二进制位数组长度m:位数组越长,误判率越低,但空间占用越高;
  2. 哈希函数个数k:哈希函数越多,误判率越低,但插入和查询的时间复杂度越高;
  3. 集合中预期元素个数n:即布隆过滤器需要存储的元素总数。

误判率公式

布隆过滤器的理论误判率P计算公式为:
P=(1−e−knm)k P = \left(1 - e^{-\frac{kn}{m}}\right)^kP=(1emkn)k
其中e是自然对数的底数。

参数选择最优解

为了最小化误判率P,哈希函数个数k的最优值为:
k=mnln⁡2≈0.693⋅mn k = \frac{m}{n} \ln 2 \approx 0.693 \cdot \frac{m}{n}k=nmln20.693nm
此时的最小误判率为:
Pmin=(12)k=e−k2nm P_{min} = \left(\frac{1}{2}\right)^k = e^{-\frac{k^2 n}{m}}Pmin=(21)k=emk2n

例如:若预期存储n=100000个元素,要求误判率P≤0.01%,则可计算出m≈1917000(约 235KB),k≈13

三、布隆过滤器的实现示例(Python)

importmathimporthashlibclassBloomFilter:def__init__(self,expected_n,error_rate=0.01):""" 初始化布隆过滤器 :param expected_n: 预期存储的元素个数 :param error_rate: 允许的最大误判率 """# 计算二进制位数组长度 mself.m=self._calculate_m(expected_n,error_rate)# 计算最优哈希函数个数 kself.k=self._calculate_k(expected_n,self.m)# 初始化二进制位数组(用整数模拟,bitarray 更高效,这里用列表简化)self.bit_array=[0]*self.m# 哈希函数列表(使用 hashlib 实现多个独立哈希)self.hash_seeds=[iforiinrange(self.k)]def_calculate_m(self,n,p):"""计算位数组长度 m"""m=-(n*math.log(p))/(math.log(2)**2)returnint(math.ceil(m))def_calculate_k(self,n,m):"""计算最优哈希函数个数 k"""k=(m/n)*math.log(2)returnint(math.ceil(k))def_hash(self,value,seed):"""单个哈希函数:将 value 映射到 [0, m-1] 索引"""# 使用 MD5 哈希,结合种子生成不同的哈希值md5=hashlib.md5((str(value)+str(seed)).encode('utf-8'))# 将哈希结果转为整数,取模得到索引returnint(md5.hexdigest(),16)%self.mdefadd(self,value):"""插入元素 value"""forseedinself.hash_seeds:idx=self._hash(value,seed)self.bit_array[idx]=1defcontains(self,value):"""查询元素 value 是否存在"""forseedinself.hash_seeds:idx=self._hash(value,seed)ifself.bit_array[idx]==0:returnFalsereturnTrue# 使用示例if__name__=="__main__":# 预期存储 10000 个元素,误判率 1%bf=BloomFilter(expected_n=10000,error_rate=0.01)# 插入元素test_set=[f"element_{i}"foriinrange(10000)]forelemintest_set:bf.add(elem)# 测试存在的元素(应返回 True)print(bf.contains("element_123"))# True# 测试不存在的元素(大概率返回 False,小概率误判为 True)print(bf.contains("nonexistent_element"))# False(或 True,误判)# 输出参数print(f"位数组长度 m:{bf.m}")print(f"哈希函数个数 k:{bf.k}")

四、布隆过滤器的特性

1. 优点

  • 空间效率极高:相比哈希表、红黑树等结构,布隆过滤器仅用二进制位数组存储,空间复杂度为O(m),远低于其他结构;
  • 时间效率高:插入和查询的时间复杂度均为O(k)k为哈希函数个数,通常是常数),效率极高;
  • 支持并行操作:哈希函数之间相互独立,插入和查询可并行执行;
  • 无数据存储:布隆过滤器不存储元素本身,仅存储二进制位,适合敏感数据场景(如密码黑名单)。

2. 缺点

  • 存在误判率:无法 100% 准确判断元素存在,误判率与mkn相关;
  • 不支持删除操作:普通布隆过滤器删除元素会导致误判率升高(多个元素共享同一组二进制位);
  • 无法获取元素本身:布隆过滤器仅记录“存在性”,无法存储元素的额外信息。

3. 改进方案

  • 计数布隆过滤器:将二进制位数组改为计数器数组(每个位置存储整数),插入时计数器加 1,删除时减 1,解决删除问题,但空间占用更高;
  • 分层布隆过滤器:将多个布隆过滤器分层,用于处理动态数据集,降低误判率;
  • 布隆过滤器联盟:多个布隆过滤器协同工作,适用于分布式系统。

五、布隆过滤器的典型应用

  1. 缓存穿透防护

    • 场景:Redis 缓存中,若查询一个不存在的 key,请求会穿透到数据库,导致数据库压力过大;
    • 方案:用布隆过滤器存储所有缓存的 key,查询前先通过布隆过滤器判断 key 是否存在,不存在则直接返回,避免穿透。
  2. 黑名单过滤

    • 场景:垃圾邮件过滤、恶意 IP 拦截、违禁词检测;
    • 方案:将黑名单中的元素存入布隆过滤器,快速判断新元素是否在黑名单中。
  3. 分布式系统去重

    • 场景:分布式爬虫去重、分布式任务调度去重;
    • 方案:多个节点共享一个布隆过滤器,判断任务/URL 是否已被处理,避免重复执行。
  4. 数据库查询优化

    • 场景:判断一个值是否存在于数据库的大表中;
    • 方案:将大表的主键存入布隆过滤器,查询前先判断,不存在则直接返回,减少数据库查询次数。
  5. 网络爬虫 URL 去重

    • 场景:爬虫爬取 URL 时,避免重复爬取相同页面;
    • 方案:用布隆过滤器存储已爬取的 URL,新 URL 先查询布隆过滤器,存在则跳过。

六、布隆过滤器与其他数据结构的对比

数据结构空间复杂度插入时间复杂度查询时间复杂度支持删除误判率适用场景
布隆过滤器O(m)O(k)O(k)不支持(普通版)海量数据的存在性判断
哈希表O(n)O(1)(平均)O(1)(平均)支持需存储元素及额外信息
红黑树O(n)O(log n)O(log n)支持有序数据的增删改查
位图(BitMap)O(m)O(1)O(1)支持整数集合的存在性判断(无哈希冲突)

布隆过滤器的核心优势是极致的空间效率,适合“允许少量误判、不支持删除、仅需存在性判断”的场景;若需精确判断或存储元素信息,则需选择哈希表等结构。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 19:42:34

Redash数据可视化终极指南:从原始数据到专业报表

Redash数据可视化终极指南:从原始数据到专业报表 【免费下载链接】redash getredash/redash: 一个基于 Python 的高性能数据可视化平台,提供了多种数据可视化和分析工具,适合用于实现数据可视化和分析。 项目地址: https://gitcode.com/Git…

作者头像 李华
网站建设 2026/6/10 14:10:10

关于 QGraphicsItemGroup 内部项目发生变化后group重新定位的问题

在项目中为了便于对组合后的图元进行管理,一般会继承 QGraphicsItemGroup 实现自己的 group 类,这样可以方便的借用 QGraphicsItemGroup 对内部图元进行管理,但同时也受到了 QGraphicsItemGroup 实现的约束。例如:QGraphicsItemGr…

作者头像 李华
网站建设 2026/6/10 0:14:35

FFmpeg开发笔记(八十九)基于FFmpeg的直播视频录制工具StreamCap

着推拉流技术的发展,现在网络直播应用很普及了,打开许多App都能看到各式各样的直播节目,包括抖音、快手、虎牙、斗鱼、B站、小红书、YY、映客等等。有关视频推拉流的FFmpeg实现参见《FFmpeg开发实战:从零基础到短视频上线》一书的…

作者头像 李华
网站建设 2026/6/10 14:09:30

现代UI组件库与设计工具的无缝集成实战指南

现代UI组件库与设计工具的无缝集成实战指南 【免费下载链接】ant-design An enterprise-class UI design language and React UI library 项目地址: https://gitcode.com/gh_mirrors/ant/ant-design 在当今快速迭代的产品开发环境中,设计与开发团队之间的协作…

作者头像 李华
网站建设 2026/6/10 15:45:22

SpringAI基于内存存储向量

一、环境信息 1、参看如下&#xff1a; SpringAI入门学习 二、测试使用 1、创建SimpleVectorStore public interface VectorStore extends DocumentWriter {default String getName() {return this.getClass().getSimpleName();}void add(List<Document> documents)…

作者头像 李华
网站建设 2026/6/10 14:42:17

Matlab/Simulink 三相四桥臂逆变器仿真模型探索

matlab/simulink三相四桥臂逆变器仿真模型 采用电压外环&#xff0c;电流内环控制策略&#xff0c;交流侧可以接不平衡负载&#xff0c;在负载不平衡的情况下依然保持输出电压对称。 直流侧电压为750V&#xff0c;可以自己定义&#xff0c;交流侧为380/220v&#xff0c;平衡负载…

作者头像 李华