惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
Recent Announcements
Recent Announcements
阮一峰的网络日志
阮一峰的网络日志
IT之家
IT之家
A
About on SuperTechFans
量子位
Engineering at Meta
Engineering at Meta
B
Blog
The Cloudflare Blog
博客园 - 【当耐特】
Hugging Face - Blog
Hugging Face - Blog
Y
Y Combinator Blog
J
Java Code Geeks
D
DataBreaches.Net
aimingoo的专栏
aimingoo的专栏
T
Tailwind CSS Blog
H
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
Stack Overflow Blog
Stack Overflow Blog
C
Check Point Blog
酷 壳 – CoolShell
酷 壳 – CoolShell

博客园 - ChatInfo

一次 Android 抓包引出的疑问:Tor Browser 桌面模式下为何出现直连请求 多模态检索开始进入工程期:用 Sentence Transformers 搭建可落地的 Multimodal RAG Etsy 把 1000 个 MySQL 分片迁进 Vitess:425TB 数据背后的真正问题不是性能,而是运维规模 普通视频也能生成4K全景:港中文团队把“空间视频”的门槛往下拉了一大截 当 AI Agent 把调用链拉长,延迟开始成为一门生意 Tailwind CSS 4.2 的真正变化:它正在把一部分前端基础设施直接做进框架 AI 写代码的时代,为什么动态语言开始显得更“便宜”了? 看外语视频神器,将本地视频通过AI技术自动生成字幕及翻译字幕 一不小心写了个免费的跨平台网络考试培训网教系统 ASP.NET Core利用拦截器 IActionFilter实现权限控制 用ASP.NET Core2重写了一款网络考试培训的免费软件 ASP.NET Core2实现静默获取微信公众号的用户OpenId ASP.NET Core2基于RabbitMQ对Web前端实现推送功能 .NET Core2使用Azure云上的Iot-Hub服务 ASP.NET Core2读写InfluxDB时序数据库 ASP.NET Core2调用Azure云上的PowerBI报表展示 利用人工智能(Magpie开源库)给一段中文的文本内容进行分类打标签 在ASP.NET Core2上操作MongoDB就是能这么的简便酷爽(自动完成分库分表) ASP.NET Core2使用Autofac实现IOC依赖注入竟然能如此的优雅简便
一次推荐系统的性能瓶颈:为什么很多工程团队最终都会用上 Bl...
ChatInfo · 2026-04-08 · via 博客园 - ChatInfo

在很多技术讨论里,性能问题往往被归结为算法复杂度。但在真实系统中,拖慢服务的常常不是复杂计算,而是那些被重复执行的简单判断。

例如推荐系统里一个看似普通的需求:不要再给用户推荐他已经看过的内容。

听起来非常简单。但当系统规模上来之后,这件事很快会变成一个真正的工程问题。

      当推荐系统进入高并发阶段

设想一个内容推荐服务。

系统每秒需要处理大约 1.8 万次请求,每个请求需要从候选池里评估大约 100 多篇内容。仅这一项,就意味着系统每秒要做超过 200 万次“是否看过”的判断。

最直观的做法,是把用户历史行为存在数据库或缓存里,然后逐条查询。

问题很快出现:

如果每次都访问存储系统,数据库或 Redis 很容易成为瓶颈;如果把完整历史放进内存,又会消耗大量资源。

于是工程团队开始问一个更现实的问题:

有没有一种方法,可以在极低成本下快速判断“这个内容大概率没看过”?

Bloom Filter 就是在这种场景下进入系统设计的。

      Bloom Filter 为什么在工程里这么受欢迎

Bloom Filter 的核心作用只有一个:

判断一个元素是否“可能存在”。

它的特点非常明确:

  • 不会出现假阴性(如果说不存在,那一定不存在)
  • 可能出现假阳性(有时会误判为存在)

从理论角度看,这似乎是个缺点。但在工程里,它恰恰是一种可以接受的交易。

回到推荐系统的例子。

如果 Bloom Filter 误判某篇文章“用户已经看过”,结果只是这篇内容没有被推荐而已。用户几乎不会察觉。

但如果不用 Bloom Filter,每个候选内容都要访问存储系统,那么系统成本会迅速上升。

换句话说:

工程团队用一点点“可能错过推荐”的概率,换来了系统吞吐量的大幅提升。

这就是典型的工程权衡。

      真正的关键:参数怎么选

很多介绍 Bloom Filter 的文章会停在算法原理,但在真实系统里,决定效果的往往是参数选择。

两个参数最重要:

  • 位数组大小(m)
  • 哈希函数数量(k)

这两个值直接决定三个指标:

  • 内存占用
  • 插入速度
  • 误判率

如果数组太小,误判率会迅速上升;如果数组太大,内存优势就会消失。

工程上通常会先确定可接受的误判率,例如 1% 或 0.1%,然后根据预计元素数量反推 Bloom Filter 的大小。

很多团队第一次上线 Bloom Filter 时会忽略一个问题:

元素数量会增长。

当过滤器逐渐接近容量上限时,误判率会明显上升。因此在长期运行的系统中,通常需要:

  • 定期重建
  • 分片 Bloom Filter
  • 或使用可扩展版本

这些都是实际系统里必须考虑的事情。

      为什么很多团队用 Go 来实现

Bloom Filter 的实现并不复杂,但它涉及大量位操作和哈希计算。

Go 在这里有几个明显优势:

第一,内存模型简单。

位数组可以直接用 byte slice 表示,读写成本非常低。

第二,性能稳定。

Go 的并发模型允许在高并发服务中安全使用共享结构。

第三,部署简单。

很多推荐服务本身就是 Go 编写的微服务,把 Bloom Filter 直接嵌入服务进程可以减少额外网络调用。

典型实现结构非常清晰:

  • 一个位数组
  • 一组哈希函数
  • Add 与 Test 两个操作

插入时设置多个 bit,查询时检查这些 bit 是否全部为 1。

逻辑简单,但在大规模请求下可以节省大量存储访问。

      Bloom Filter 不是万能解

虽然它很高效,但并不是所有场景都适合。

以下几种情况通常不适合:

如果误判成本极高,例如金融交易或权限系统,那么概率型结构就不合适。

如果数据量很小,用普通 HashSet 反而更简单。

如果系统必须支持删除操作,标准 Bloom Filter 也无法直接做到,需要使用 Counting Bloom Filter 等变种。

因此工程团队真正需要回答的不是“这个结构厉不厉害”,而是:

误判带来的业务代价是否可以接受。

只要这个问题的答案是“可以”,Bloom Filter 往往就是一个非常划算的方案。

      一条典型的落地路径

很多团队最终都会以类似方式引入 Bloom Filter:

第一步,在服务层加入过滤器作为预检查。

先判断内容是否可能出现过,如果结果是“肯定没有”,就直接跳过数据库查询。

第二步,监控误判率。

通过日志统计 Bloom Filter 拦截率和真实命中率,确认参数是否合理。

第三步,规划扩展策略。

随着用户行为增长,提前准备新的过滤器或分片结构,避免误判率失控。

当这套机制稳定之后,系统会得到一个很直接的收益:

数据库访问量显著下降,而推荐服务的吞吐能力明显提升。

很多工程优化看起来是算法问题,本质却是系统成本问题。

Bloom Filter 的价值就在这里——

它不是让系统更聪明,而是让系统在大规模运行时更便宜。

声明

关注微信公众号解锁更多技术资讯,感谢您的支持!