Turbopuffer重构存储架构,ANN降级为二级索引

Turbopuffer重构存储架构,ANN降级为二级索引。此次变更旨在优化文件布局与查询方式,提升文本、正则及向量搜索性能。新机制突破原有矢量主导架构在聚合操作上的局限,适应更多非典型搜索场景。

2026年9月30日,Turbopuffer 工程师 Dan Harrison 宣布对轮缓冲器(turbopuffer)的存储架构进行重大调整。此次变更旨在重构文件与索引布局、写入压缩及查询方式,以全面提升文本、正则表达式及向量搜索的性能。

ANN索引由主变次

Turbopuffer v1 作为无服务器向量数据库推出时,核心策略是利用对象存储作为数据源以降低成本,并通过分层 NVMe SSD/内存缓存保障性能。这一权衡方案得到了 Cursor 和 Notion 等早期客户的验证。随后演进的 v2 版本增强了文本和正则搜索能力,并拓展至 Linear 同步引擎等非典型搜索场景。尽管查询引擎不断迭代以支持新用例,但其底层存储架构始终未变:近似最近邻(ANN)向量索引仍是所有其他索引和查询计划围绕的核心主索引。这种设计在 GROUP BY 和聚合操作等特定查询计划上存在局限。

鉴于矢量主导架构已接近其设计极限,团队决定转向新的主索引机制,将 ANN 降级为“仅仅是另一个”二级索引。以下回顾从 Turbopuffer v1 到当前状态的技术演变路径。

v1: 单一 ID 与向量结构

在 Turbopuffer 初版中,文件仅包含一个 ID 和一个向量。当时业界主流倾向于基于图形的向量索引,但团队发现层次聚类索引更适配对象存储特性。初期采用 SPANN 算法,后迁移至 SPFresh 以支持增量索引。向量被聚合成群,中心点再进一步聚合形成树状结构。


                      ┌───────────────────┐
                      │   root centroid   │
                      └───────────────────┘
                     ╱          │          ╲
                    ╱           │           ╲
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│   leaf centroid   │ │   leaf centroid   │ │   leaf centroid   │
└───────────────────┘ └───────────────────┘ └───────────────────┘
      ╱       ╲             ╱       ╲             ╱       ╲
     ╱         ╲           ╱         ╲           ╱         ╲
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ vector │ │ vector │ │ vector │ │ vector │ │ vector │ │ vector │
└────────┘ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘

      ┌───────────────┐
      │ root centroid │
      └───────────────┘
        ╱     │     ╲
       ╱      │      ╲
┌────────┐┌────────┐┌────────┐
│  leaf  ││  leaf  ││  leaf  │
│centroid││centroid││centroid│
└────────┘└────────┘└────────┘
   ╱  ╲      ╱  ╲      ╱  ╲
┌───┐┌───┐┌───┐┌───┐┌───┐┌───┐
│vec││vec││vec││vec││vec││vec│
└───┘└───┘└───┘└───┘└───┘└───┘

该结构在一个呈现为键值映射的存储层上实现。每个集群分配一个 ClusterId,集群内的向量分配密集的 LocalId。


K::Vector(C0L0) = vec![0.45, 0.32, ...]
K::Id(C0L0) = 7
K::Vector(C0L1) = vec![-0.28, 0.96, ...]
K::Id(C0L1) = 13


K::Vector(C1L4) = vec![0.64, -0.48, ...]
K::Id(C1L4) = C0

所有数据均通过 ClusterId 和 LocalId(如 C0L1)组合成的键进行寻址,即 ANN 地址。在此架构下,ANN 索引被视为主要索引。

v2: 属性过滤与全文搜索

从 v1 过渡到 v2 的标志是引入了两个新的查询计划:属性过滤和全文搜索。

属性过滤

为满足用户在向量搜索中添加属性过滤的需求,并提升过滤速度与召回率,团队将其建模为反向索引。

K::AttrIndex("family", "Alcidae") -> vec![C0L3, C1L2, C1L3, ...]
K::AttrIndex("genus", "Fratercula") -> vec![C0L3, C1L2, C1L9, ...]

对于投影操作(include_attributes),文档属性与 ID 及向量一同存储。

K::Vector(C0L0) = vec![0.45, 0.32, ...]
K::Id(C0L0) = 7
K::Attr(C0L0, "family") = "Alcidae"
K::Attr(C0L0, "genus") = "Fratercula"

全文搜索

BM25 全文搜索是另一项关键功能。类似属性搜索,全文搜索首先定位包含查询术语的文档(倒排列表)。FTS 索引还存储了 BM25 评分所需的元数据(词频、文档长度):

K::FTS("description", "Atlantic") -> vec![(C0L0, 2, 37), (C9L4, 1, 42), ...]
K::Attr(C0L0, "description") -> "A sharply dressed black-and-white seabird with a \
huge, multicolored bill, the Atlantic Puffin is often \
called the clown of the sea. It breeds in burrows on \
islands in the North Atlantic, and winters at sea."

此后,系统陆续推出了聚合、正则搜索、模糊匹配、稀疏向量搜索及属性排序等其他索引结构和查询引擎。

矢量主导索引面临的挑战

矢量主导架构之所以长期保留,是因为其在对象存储上的 ANN 搜索表现优异,支持单个索引容纳超过 1000 亿个向量。任何重大架构变动都可能导致 ANN 性能回退。然而,非向量查询形状在该架构下面临三大瓶颈:存储放大、写入放大以及有限的向量化。

存储放大

目前,Turbopuffer 将每个文件的全部内容存储在其 ANN 地址下。当文件仅含一个向量时,非向量数据只需存储一次。但在多向量表示场景(如文档嵌套或迟交互模型)中,必须为每个向量复制完整内容,导致显著的空间浪费。

写入放大

每当文档插入、更新或删除时,SPFresh 可能会重新平衡向量以维持集群质量,防止召回率下降。由于文档所有内容均绑定于向量 ANN 地址,这种重平衡会级联移动整个文档内容及引用它的反向索引(属性和 FTS)。单个向量的更新可能触发数百个属性及其索引的移动,严重制约索引吞吐量优化空间。

有限的向量化

现代查询引擎依赖向量化技术,通过对数据块执行紧凑循环来分摊固定开销、提升压缩效率并保持 CPU 流水线满载。例如,DuckDB 批次大小为 2,048 行,ClickHouse 可达约 65k,Lucene 倒排列表块包含 256 个文档。相比之下,Turbopuffer 的 ANN 索引最佳性能区间仅为 100–200 个文档的集群大小。即使某些查询计划偏好更大批次以保持 CPU 满载,仍受限于 ANN 主索引的小块尺寸。

此前实验显示,将全文搜索发帖列表划分在 ANN 集群边界之外,可使索引缩小 10 倍,查询速度提升 20 倍。这是因为发帖列表可独立存储并指向文档,而聚集和其他扫描需读取按组存储的文件块。只要 ANN 地址为主键,这些操作的块大小就被限制在集群规模内,无法利用更大的理想块尺寸。

去中心化主索引:Turbopuffer v3

解决上述问题的核心思路是摒弃 ANN 地址作为主键。这正是 Turbopuffer v3 的关键变革。本月早些时候,v3 实现了 100% CI 测试通过。当前阶段首要关注正确性,后续将致力于性能优化。团队计划在 v3 正式发布前的未来几周内公开基准测试结果,力求达到甚至超越现有性能平衡。

Turbopuffer 是一个快速搜索引擎,托管超过 1 万亿个文件,处理每秒超过 1000 万次写入,服务每秒超过 2.5 万个查询。

评论 0

0/500

评论需审核后展示,请文明发言

💬
还没有评论,来说两句

相关阅读