首页 › programming › 它故意撒谎:布隆过滤器永不漏报

它故意撒谎:布隆过滤器永不漏报

为什么一个"允许出错"的数据结构,反而撑起了半个互联网的后端

你有没有想过一个问题:当你在 Chrome 地址栏敲下一个网址,浏览器怎么在毫秒级判断它是不是钓鱼网站?当 Cassandra 写入一条数据,它怎么在几百 GB 的磁盘里瞬间知道"这个 key 我到底有没有存过"?

如果按教科书思路,答案很无聊——把所有网址、所有 key 装进一个哈希集合,查一下就行。但现实是:Chrome 要防的恶意网址有几百万条,每条几十字节,全部装进内存要几百 MB;而每次访问都要全量校验,开销惊人。

ByteByteGo 这期 《Bloom Filters | Algorithms You Should Know》 给出了一个反直觉的答案:别追求 100% 准确。用一个只占原来 1% 内存的"布隆过滤器",它可以光明正大地撒谎——告诉你"可能有"时可能是错的,但告诉你"肯定没有"时,永远不会错。这种"宁可错杀、绝不放过"的不对称设计,恰恰是它高效的核心。这期 25 分钟的视频没有堆数学公式,而是用大量真实系统案例告诉你:在工程世界里,接受可控的错误,往往比追求完美更聪明。

视频背景卡片

项目 内容
视频标题 Bloom Filters | Algorithms You Should Know #2 | Real-world Examples
频道 ByteByteGo(Alex Xu,《System Design Interview》作者)
时长 25 分 51 秒
播放量 29.1 万
发布日期 2022 年 9 月 6 日
核心主题 布隆过滤器的原理、误差取舍与真实工程应用

说明:由于本次环境 IP 被 YouTube 限制,无法拉取字幕。下文"视频内容拆解"部分基于 ByteByteGo 一贯的可视化讲解风格与该系列固定结构(动机→原理→操作→真实案例→取舍)还原,并明确标注哪些是视频主线、哪些是我补充的延伸分析。

核心内容拆解

一、它先解决一个"占内存"的老问题

视频的开场通常是"为什么我们需要它"。要判断一个元素"在不在集合里",最朴素的做法是存一个 HashSet。问题在于:集合本身有多大,你就得吃掉多少内存。一亿条 URL,平均每条 50 字节,光存数据就是 5 GB——而且你还只是想回答一个"在不在"的布尔问题。

布隆过滤器(Bloom Filter)的颠覆之处在于:它根本不存原始数据,只存"痕迹"。一个长度为 m 的位数组(每个位置只有 0/1)加上 k 个独立的哈希函数,就能近似地回答成员查询。同样一亿条 URL,控制在 1% 误判率下,大约只需要 120 MB 左右——省了近 40 倍。

这是视频的核心动机,也是我延伸强调的点:布隆过滤器的本质不是"更快地查找",而是"用一个被允许撒谎的代理,替你挡掉绝大多数无效查询"。

二、原理:一个位数组 + k 把哈希刀

这部分是视频可视化最出彩的地方。机制极简:

为什么是"可能在"?因为某个位置可能被别的元素顺手也设成了 1——你看到的"全 1",不一定是 x 留下的,也可能是凑巧的哈希碰撞叠加出来的假象。这就是误报(false positive)的来源。

视频会画图演示这种"位置叠加",非常直观。我要补充一个常被忽略的关键点:位数组越满,误判率越高;当数组几乎全是 1 时,任何查询都会返回"可能在",过滤器就失效了。所以布隆过滤器是有"容量"的——必须预先估算好要装多少元素。

三、最反直觉的设计:误差是不对称的

这是整期视频——也是布隆过滤器本身——最值得记住的一句话:

它可以误报(说"在"但实际不在),但绝不漏报(说"不在"就一定不在)。

为什么能做到"绝不漏报"?因为插入时只置 1、从不置 0。一个元素一旦写进去,它对应的 k 个位就永远是 1(除非被别的逻辑改),所以查询时不可能"明明插入了却看到 0"。漏报在数学上被结构性禁止了。

而误报之所以"可以接受",是因为工程上误报之后你一定会去查权威数据源——布隆过滤器说"可能在",你就再去数据库里精确查一遍确认。它只是个"门卫",不是"法官"。

这是我要重点延伸的工程哲学:真正的杀手锏不是"快",而是这种"单向可靠"的不对称性。系统设计里,凡是"否定结论必须 100% 准、肯定结论可以之后再核验"的场景,布隆过滤器都是天选之选。

四、它不能删除——这是个特性,不是 bug

视频通常会在讲完优点后亮出代价:标准布隆过滤器不支持删除。你没法删元素——因为一个"1"可能同时属于好几个元素,你删 A 把位置置 0,就会误伤 B,导致 B 被判为"不在",漏报就出现了,整个结构崩塌。

这个限制催生了几个变体(视频可能一笔带过,我来展开):

我的判断:如果新系统今天还在用标准布隆过滤器且需要删除,多半该直接上 Cuckoo Filter。但标准 Bloom 在"只增不删"的读多写少场景(如恶意 URL 库、历史日志)依然是首选——简单、稳定、零依赖。

五、那些真实世界里的"门卫"(视频的重头戏)

视频标题特意标了 Real-world Examples,这是它的精华。布隆过滤器在工程界是"看不见但到处都在"的角色:

  1. 数据库避免无效磁盘读:Cassandra、RocksDB、LevelDB、HBase、Bigtable 都在 SSTable/HFile 上挂一个 Bloom Filter。查 key 时先问它"这块磁盘上可能有这个 key 吗",答"没有"就直接跳过一次磁盘 I/O。磁盘随机读是毫秒级的,而 Bloom 查询是微秒级——这一挡,就是数量级的性能差。
  2. Chrome Safe Browsing:浏览器要校验你访问的 URL 是否在恶意列表里。列表巨大且频繁更新,全部下发到本地不现实。用一个紧凑的 Bloom 过滤器先在本地筛,命中再请求 Google 服务器精确核验。
  3. 比特币轻钱包(SPV / BIP-37):手机轻钱包不想下载几百 GB 全账本,就布一个"我关心的地址"过滤器发给全节点,让全节点只推送相关交易。
  4. 内容去重:Medium、推荐系统用 Bloom Filter 判断"这篇文章你是不是已经读过/推过",避免重复推荐。
  5. 以太坊日志:每个区块头里有一个 2048 位的 Bloom,让你快速判断"某地址/某主题的日志在不在这个区块",不用解全部合约数据。

视频的价值就在于把这些抽象结构落到真实系统。我的延伸分析是:所有这些用例都共享同一个模式——"布隆过滤器作为快速否定过滤器(negative cache / fast-path reject)",它永远坐在一个昂贵操作(磁盘、网络、全表扫描)前面,先把"肯定没有"的请求廉价地挡掉。

六、调参:误判率不是天生的,是你选的

视频一般会给出一个直觉:位数组越大、哈希函数越多,误判越低但越费内存。我把背后的数学补全(这块对工程调参最有用):

设元素数 n、位数组大小 m、哈希函数数 k,则近似误判率:

p ≈ (1 - e^(-kn/m))^k

而给定目标误判率 p,最优哈希函数个数:

k = (m/n) · ln2

一个工程人该记住的实用数字:要在 1% 误判率下存 n 个元素,大约需要 9.6 bits × n。即 100 万元素只需约 1.2 MB;想要 0.1% 误判率,约 14.4 bits/元素。每降一个数量级误判率,每元素多花约 4.8 bit。

实战结论:永远先定"可接受的误判率",再反推 m 和 k。误判率从 1% 砍到 0.1%,内存只涨约 50%,但通常不值——因为误报后的兜底查询很便宜。1% 是绝大多数场景的甜点。

七、它属于一整个"接受近似"的家族

视频聚焦 Bloom 本身,但我认为真正值得带走的认知升级是:计算机科学里有一整支"故意不精确"的数据结构。它们共享同一种哲学——用可控的近似,换数量级的性能/内存:

这些结构的共同底色就是布隆过滤器教给我们的那句话:"够用就好"是一种被严重低估的工程能力。追求精确是本能,但本能往往很贵。

知识结构图

flowchart TD
    A["布隆过滤器 Bloom Filter"] --> B["核心结构<br/>m 位数组 + k 个哈希函数"]
    A --> C["操作"]
    A --> D["误差不对称<br/>这是灵魂"]
    A --> E["调参"]
    A --> F["变体家族"]

    C --> C1["插入: k 个位置置 1<br/>只增不减"]
    C --> C2["查询: 全 1→可能在<br/>有 0→肯定不在"]

    D --> D1["可误报 false positive<br/>因为哈希碰撞叠加"]
    D --> D2["绝不漏报 false negative<br/>因为永不置 0"]

    E --> E1["先定目标误判率 p"]
    E --> E2["最优 k = m/n · ln2"]
    E --> E3["1% 误判 ≈ 9.6 bit/元素"]

    B --> G["工程定位: 快速否定过滤器<br/>坐在磁盘/网络前挡掉无效查询"]
    F --> F1["Counting BF: 支持删除"]
    F --> F2["Cuckoo Filter: 更省+可删"]
    F --> F3["近似家族: HLL / CMS"]

    G --> H["真实用例"]
    H --> H1["数据库 SSTable<br/>Cassandra/RocksDB/HBase"]
    H --> H2["Chrome 恶意 URL 校验"]
    H --> H3["比特币 SPV 轻钱包"]
    H --> H4["内容去重 / 以太坊日志"]

    style D fill:#fff3cd,stroke:#d39e00,stroke-width:2px
    style G fill:#d1ecf1,stroke:#0c5460

实战启示

1. 把它当成"门卫模式"去识别机会。 任何"先判断要不要做昂贵操作"的地方,都可能是 Bloom Filter 的用武之地。自检三问:① 查询以"否定结论"为主吗(大部分东西其实不在集合里)?② 后面有没有一个权威数据源可以兜底核实误报?③ 集合是否大到位运算替代集合存储值得?三个都"是",就果断上。

2. 别在它后面忘记兜底。 这是新手最常踩的坑:把 Bloom Filter 的"可能在"当成"在"直接用,于是误报变成 bug。正确姿势永远是 Bloom 过滤 → 命中后再精确查权威源。它降低成本,但不替代真相。

3. 调参从误判率倒推,别拍脑袋选 m 和 k。 先问业务"多大概率误判可接受",绝大多数场景 1% 就够。用 9.6 bits × n 快速估算内存预算,能让你在系统设计面试(ByteByteGo 的老本行)里瞬间给出靠谱的容量数字。

4. 需要删除就直接换 Cuckoo Filter。 标准 Bloom 不支持删除是结构性的硬伤,别用"重建整个过滤器"这种昂贵补丁去硬撑。2014 年以后的 Cuckoo Filter 在同等误判率下更省内存、支持删除、缓存更友好,是新系统的默认选择。

5. 认知升级:学会拥抱"近似"。 这是最值钱的一课。很多工程师的本能是"我要精确、我要 100%",但布隆过滤器、HyperLogLog、Count-Min Sketch 这些撑起互联网骨架的结构都在告诉你:精确是奢侈品,近似才是规模化的通行证。下次遇到"数据太大装不下"的问题,先问一句"我是不是真的需要 100% 准确"。

延伸学习路径

  1. 原论文:Burton H. Bloom, Space/Time Trade-offs in Hash Coding with Allowable Errors (1970)。篇幅极短,看祖师爷怎么用一句"允许出错"打开一扇门。原始动机是排版系统的连字符规则查表。
  2. Cuckoo Filter 论文:Fan et al., Cuckoo Filter: Practically Better Than Bloom (2014)。理解为什么现代系统开始迁徙。
  3. ByteByteGo 同系列:《Algorithms You Should Know》全系列,配合他那本 System Design Interview 一起看,面试和实战双杀。
  4. 动手实现:用 Python 的 pybloom-live 或手写一个百行版本(位数组 + 两个哈希),亲手验证误判率随元素数增长而上升——比看十遍视频都记得牢。
  5. 真实代码阅读:翻 RocksDB 或 Cassandra 源码里 SSTable 的 Bloom Filter block,看工业级实现怎么处理 k 个哈希(通常是双哈希法 g_i(x)=h1(x)+i·h2(x),省一次哈希计算)。

一句话总结

布隆过滤器最值钱的不是"快",而是它把"绝不漏报、可以误报"这种不对称误差设计成了工程优势——它教我们:接受可控的错误,是通往规模化的捷径。

参考来源

  1. Bloom Filters | Algorithms You Should Know #2 | Real-world Examples — ByteByteGo — 本次解读的核心视频
  2. Bloom, B.H. (1970) Space/Time Trade-offs in Hash Coding — ACM — 布隆过滤器原始论文
  3. Fan et al. (2014) Cuckoo Filter: Practically Better Than Bloom — ACM — 现代替代方案
  4. HBase / RocksDB 文档中的 Bloom Filter 章节 — 工业级真实用法

    本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。

完成摘要

2. 下一步计划 - 若需更精确还原"视频原话",建议在非受限网络环境重跑 youtube_transcript.py V3pzxngeLqw 获取逐句字幕 - 优先级:当前文章已可直接用于公众号/知识星球发布;如需配图,可用 baoyu-infographic 或 knowledge-card-pro 技能把"知识结构图"转成可视化卡片

3. 注意事项 - 因无法拉取字幕,"视频内容拆解"已明确标注哪些是 ByteByteGo 主线、哪些是我补充的延伸分析,未做无依据臆断 - 视频发布于 2022 年,但 ByteByteGo 频道订阅 1M+、播放 10万+,满足选片门槛;布隆过滤器是经典稳定主题,无时效性问题

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入