Notion向量搜索两年回顾:规模扩大10倍,成本降至1/10

Lobsters Hottest 新闻

摘要

Notion分享了其向量搜索基础设施在过去两年中实现规模扩大10倍、成本降低90%的经验,详细介绍了从上线到处理数百万工作区的架构演变。

<p><a href="https://lobste.rs/s/1xbtlo/two_years_vector_search_at_notion_10x">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/07/22 10:20

# Notion 向量搜索的两年历程:规模扩大 10 倍,成本降至十分之一 来源:https://www.notion.com/blog/two-years-of-vector-search-at-notion 当我们于 2023 年 11 月推出 Notion AI Q&A(https://www.notion.com/blog/introducing-q-and-a)时,我们深知自己踏上了一条雄心勃勃的旅程。不过,我们完全没有预料到扩展速度会如此之快,也没想到一旦步入正轨,成本优化幅度能如此之大。这就是我们如何在过去两年里将向量搜索基础设施规模扩大 10 倍,同时将成本降低 90% 的故事。 ## 什么是向量搜索,为什么 Notion AI 要使用它? 传统的关键字搜索只能匹配确切的词语,因此像“团队会议笔记”这样的查询可能会错过标题为“组站会摘要”的内容,尽管它们含义相同。 向量搜索通过将文本转化为语义嵌入来解决这个问题:在高维空间中,相似的概念会聚集在一起。这使得我们能够基于含义检索相关内容,而不是局限于精确的措辞。 这对 Notion AI 至关重要,它通过搜索用户工作区以及 Slack 和 Google Drive 等连接工具中的内容来回答自然语言问题。 ## 第一部分:扩展至数百万个工作区 当我们于 2023 年 11 月推出时,我们的内容摄取索引管道有两条路径: - **离线路径**:在 Apache Spark 上运行的批量作业,用于将已有文档分块、通过 API 生成嵌入,然后将向量批量加载到我们的向量数据库中。 - **在线路径**:通过 Kafka 消费者进行实时更新,处理页面编辑事件。 这种双路径架构使我们能够高效地接入大型工作区,同时保持实时工作区的更新延迟低于一分钟。 我们的向量数据库运行在将存储和计算耦合的专用“pod”集群上,能够处理大规模的多租户负载。我们设计了一种与我们的 Postgres 设置(https://www.notion.com/blog/sharding-postgres-at-notion)类似的分片策略,使用工作区 ID 作为分区键,并通过基于范围的分区路由到正确的索引。单个配置中保存了所有分片的引用。 2023 年 11 月的发布架构 发布立即带来了巨大且压倒性的需求。我们很快积累了一个有**数百万个工作区**的等候名单,他们都渴望使用 Q&A,我们需要在保持质量和性能的同时,尽可能快地将它们接入。 ### 空间不足 发布仅仅一个月后,我们最初的索引就接近容量上限。如果空间耗尽,我们将被迫暂停接入——从而减缓 AI 功能的推广,并推迟为新用户创造价值。我们面临一个典型的扩展困境: - **增量重新分片**:将数据克隆到另一个索引,删除一半,然后随着新客户的接入每两周重复一次。 - **重新分片至最终预期容量**:我们选择的向量数据库提供商按数据库运行时间收费,过度配置的成本高得令人望而却步。 我们选择了一条与之前 Postgres 分片设置不同的路径。我们不再进行重新分片操作,而是当一组索引接近容量时,配置一组新索引,并将所有新工作区的接入导向那里。每组索引都有一个“代次”ID,用于决定读写操作的去向。这样我们就可以继续推进,而无需停下来进行任何重新分片操作。 空间不足 ### 扩展规模 发布时,我们每天只能接入几百个工作区。按照这个速度,要处理数百万的等候名单需要几十年。通过 Airflow 调度、流水线化以最大化吞吐量以及 Spark 作业调优,我们加快了接入速度: - 每日接入能力:**提升 600 倍** - 活跃工作区:**增长 15 倍** - 向量数据库容量:**扩展 8 倍** 到 2024 年 4 月,我们清空了 Q&A 的等候名单!然而,管理多代次数据库虽然帮助我们度过了超高速增长阶段,但在运营上变得复杂且昂贵。我们需要一个更好的架构。 ## 第二部分:成本降低 2024 年 5 月,我们将整个嵌入工作负载从原有的专用硬件“pod”架构迁移到了新的无服务器架构,该架构将存储与计算解耦,并按使用量而非运行时间收费。 带来的好处立竿见影且非常显著:与高峰期相比**成本降低 50%**,每年节省数百万美元。无服务器设计的额外好处包括消除了存储容量限制(这曾是扩展的主要瓶颈),并通过不再需要预先配置容量简化了运营。 尽管节省了这么多资金,但仅向量数据库的年运行成本仍然高达数百万美元。我们知道还有进一步的优化潜力可以挖掘。 ### turbopuffer 评估与迁移(2024 年 5 月 – 2025 年 1 月) 在我们进行向量数据库初期成本节省工作的同时,我们对替代搜索引擎进行了全面评估,turbopuffer(https://turbopuffer.com/)成为一个极具吸引力的选项,预计成本会大幅降低。 当时,turbopuffer 是搜索领域的新秀,完全基于对象存储构建,以实现性能和成本效益。其架构(https://turbopuffer.com/docs/architecture)符合我们的需求:既支持托管模式,也支持自带云部署模式,并且能轻松批量修改存储的向量对象。经过成功评估后,我们承诺在 2024 年底将所有数十亿对象的负载迁移到 turbopuffer。 由于我们正在更换提供商,我们趁此机会全面重构了整个架构: 1. **全面重新索引**:我们提高了离线索引管道中的写入吞吐量,以在 turbopuffer 中重建语料库。 2. **嵌入模型升级**:在迁移过程中,我们切换到了更新、性能更好的嵌入模型。 3. **架构简化**:turbopuffer 将每个命名空间视为一个独立索引,无需担心分片或代次路由问题。 4. **逐步切换**:我们一次迁移一个代次,在迁移到下一个之前验证正确性。 结果: - 搜索引擎支出**降低 60%** - AWS EMR 计算成本**降低 35%** - p50 生产查询延迟从 70-100ms **改善至 50-70ms** ### 页面状态项目(2025 年 7 月) 我们的下一个重大优化解决了索引管道中的一个根本性效率问题。Notion 页面可能非常长,因此我们将每个页面分块为片段,对每个片段进行嵌入,然后将其连同元数据(如作者和权限)加载到向量数据库中。 base example v3 在最初的实现中,每当页面或其属性被编辑时,我们会重新分块、重新嵌入并重新上传该页面中的所有片段——即使只改动了一个字符。我们的挑战在于快速识别哪些内容发生了变化以及需要重新完成哪些工作。 我们关注两种变化: 1. **页面文本本身**:嵌入需要更新。 2. **页面或文本的元数据**:元数据需要更新。 为了检测变化,我们为每个片段跟踪两个哈希值:一个基于片段文本,另一个基于所有元数据字段。我们使用了 xxHash 算法的 64 位变体,因为它在易用性、速度和低碰撞率之间取得了良好平衡,并且存储占用小。 我们选择 DynamoDB 作为缓存方案,因为它提供快速的插入和查询。每个页面有一条记录,包含该页面上所有片段的结构体,以及它们的文本哈希和元数据哈希。 Screenshot 2026-02-18 at 2.19.11 PM **情况 1:页面文本发生变化** 想象一下,赫尔曼·梅尔维尔正在撰写《白鲸记》,并在页面中间进行了一次编辑。以前,我们会重新嵌入并加载整个页面。现在,我们对页面进行分块,从 DynamoDB 获取页面的先前状态,并比较所有文本哈希值。我们检测出哪些片段发生了变化,并只重新嵌入和重新加载这些片段到向量数据库中。 case 1 **情况 2:元数据发生变化** 现在,梅尔维尔准备出版《白鲸记》——他将权限从仅自己修改为所有人。我们在每个片段上都存储了权限等元数据,但更改元数据不会影响嵌入。以前,我们仍然需要重新嵌入并加载整个页面。现在,我们对页面进行分块,从 DynamoDB 获取页面的先前状态,并比较所有文本哈希和元数据哈希。我们发现所有文本哈希值相同,但所有元数据哈希值都不同。这意味着我们可以完全跳过嵌入过程,只需向向量数据库发送一个 PATCH 命令来更新元数据,这是一个成本低得多的操作。 case 2 通过这两项改动,我们实现了**数据量减少 70%**,从而节省了嵌入 API 费用和向量数据库写入成本。 ### **在 Ray 上进行嵌入索引(2025 年 7 月至今)** 2025 年 7 月,我们着手将近实时嵌入管道迁移到运行在 Anyscale(https://www.anyscale.com/)上的 Ray(https://www.ray.io/)。 Ray 是一个开源项目,许多大公司会组建内部团队围绕它进行开发(例如 Spotify(https://engineering.atspotify.com/2023/02/unleashing-ml-innovation-at-spotify-with-ray))。然而,在 Notion,我们没有专门的机器学习基础设施团队,而 Anyscale(由 Ray 原团队提供的托管版 Ray)为我们提供了机器学习平台即服务。 这一战略转变同时解决了多个痛点: - **“双重计算”问题**:在 EMR 上运行 Spark 进行预处理(分块、转换、编排 API 调用),然后还要按 token 向嵌入 API 提供商付费。 - **嵌入端点可靠性**:我们依赖提供商的 API 稳定性来保持搜索索引的新鲜度。 - **笨拙的流水线**:为了平滑依赖端点的流量并避免 API 速率限制,我们实现了自己的流水线设置,将在线索引 Spark 作业拆分为多个作业,通过 S3 传递数据批次。 那么为什么是 Ray 和 Anyscale? - **模型灵活性**:Ray 让我们可以直接运行开源嵌入模型,而不受外部提供商的限制。随着新模型的发布,我们可以立即尝试并采用它们。 - **统一计算**:通过将预处理和推理整合到单一计算层,我们消除了双重计算问题。 - **GPU/CPU 流水线**:Ray 原生支持在同一台机器上将 GPU 绑定的推理与 CPU 绑定的预处理进行流水线化,保持高利用率。 - **开发者效率**:Anyscale 的集成工作区让我们的工程师可以直接从他们喜欢的工具(Cursor、VSCode 等)编写和测试数据管道,而无需配置基础设施。 - **更低的查询延迟**:自托管嵌入消除了第三方 API 从关键路径中的一跳,从而显著降低了面向用户的搜索的端到端延迟。 Ray 原生支持在同一节点内将 CPU 绑定的任务(分块、检测页面状态)与 GPU 绑定的嵌入生成进行流水线化。来源:Anyscale。https://www.anyscale.com/glossary/ray-vs-apache-spark-technical-differences 通过将我们的嵌入生成管道从 Spark 迁移到 Ray,我们预计嵌入基础设施成本将**降低 90% 以上**。这项工作仍在推进中,但初期结果令人鼓舞。 ### **在 Ray 上进行嵌入服务(2025 年 7 月至今)** 当用户或智能体搜索 Notion 时,我们需要即时嵌入查询。在计算完成之前,我们无法搜索向量数据库,因此我们对延迟非常敏感。然而,托管大参数嵌入模型(例如 Hugging Face 上的模型)可能会比较棘手。你需要考虑从高效的 GPU 分配到入口路由、复制和自动扩缩等方方面面。 Ray Serve(https://docs.ray.io/en/latest/serve/index.html)提供了开箱即用的解决方案。它允许我们将开源嵌入模型包装成一个持久部署,常驻在 GPU 上。我们可以配置从动态请求批处理到复制的一切。模型服务代码看起来就像普通的 Python 代码,而计算、复制和自动扩缩配置则是普通的 yaml 文件。 ## 展望未来 随着我们持续扩展,我们对未来的机会充满期待: - **扩展数据源**:我们正在增加连接更多工具(https://www.notion.com/help/notion-ai-connectors)的能力,为用户提供更全面的答案。 - **模型演进**:随着该领域的快速发展,我们正在持续评估新的嵌入模型——Ray 赋予我们快速采用它们的能力。 - **管道优化**:基础设施工作永无止境。我们一直在寻找新方法,让一切更快、更便宜、更可靠。 - **Notion 智能体:** 自定义智能体(https://www.notion.com/product/custom-agents)(即将推出)将利用 AI 和向量搜索,在你的 Notion 工作区和连接的应用中为智能体提供正确的上下文,使其能够像队友一样理解信息,从而自主完成工作流程。 如果你对这些挑战感兴趣,我们正在招聘(https://www.notion.com/careers)。

相似文章

Mongo 的向量搜索性能

Reddit r/LocalLLaMA

本文讨论了 MongoDB 向量搜索功能的性能,可能与其他解决方案进行了比较,或强调了针对 AI 工作负载的改进。

@hasantoxr:向量数据库不再是云产品。它们正在变成 pip install。一个名为 turbovec 的新开源项目……

X AI KOLs Timeline

一个名为 turbovec 的开源项目在 GitHub 上获得了 1 万星标。它是一个基于 Rust、带有 Python 绑定的向量索引,使用谷歌研究的 TurboQuant 算法将嵌入压缩到接近理论香农极限,使得完全本地的 RAG(检索增强生成)成为可能——1000 万文档仅需 4 GB RAM,且搜索速度快于 FAISS。