向量数据库,安息吧

查看原文 HN 讨论

文章摘要

这是 turbopuffer 工程师 Dan Harrison 于 2026 年 9 月 30 日发表的一篇技术博客,也是 turbopuffer v3 开发日志的第一篇。标题耸动,但内容其实相当克制:turbopuffer 要改掉自家存储架构里最核心的一个设计,不再让 ANN(近似最近邻)向量索引充当主索引,而是把它降级为”又一个”二级索引。文章的任务是讲清楚为什么要这么做。

从 v1 到 v2 的演化。 turbopuffer 最初是一个 serverless 向量数据库,专门把向量检索做得极其便宜、速度也说得过去:以对象存储作为唯一可信数据源,换来成本优势;再用 NVMe SSD 和内存做分层缓存,换来性能。Cursor 和 Notion 是最早验证这套取舍的客户。v1 里的文档只有一个 ID 和一个向量。当时主流观点偏向图结构的向量索引(如 HNSW),但他们发现层次聚类索引更适合对象存储,先用了 SPANN,后来迁移到支持增量建索引的 SPFresh:向量被聚成簇,簇的质心再聚类,层层向上形成一棵只有一个根的树。底层存储是一个键有序且唯一的 KV 映射,每个簇有 ClusterId,簇内向量有稠密的 LocalId,两者合起来叫”ANN 地址”(例如 C0L1),所有数据都以它为键。这就是”ANN 索引即主索引”的含义。

到了 v2,客户需要按属性过滤,turbopuffer 用倒排索引把属性值映射到文档的 ANN 地址;为了支持返回字段,文档属性也和 ID、向量存在一起。BM25 全文检索同理,倒排列表(postings)里存的是 ANN 地址加词频和文档长度。此后又陆续上线了聚合、正则搜索、模糊匹配、稀疏向量检索、按属性排序,全部建立在同一个以向量为主的存储布局之上。turbopuffer 也开始被用于搜索之外的场景,比如 Linear 的同步引擎。

为什么撑不下去了。 作者先承认这个设计对对象存储上的 ANN 检索”非常、非常好用”:单个索引已能承载 1000 亿以上的向量,在 1000+ QPS 下 p99 读延迟 200 毫秒。正因如此,任何大改动都有让向量性能倒退的风险。但这种布局让非向量查询很难做到业界一流,问题主要有三个:

解决办法。 说起来很简单:不再以 ANN 地址为键。这正是 v3 的核心改动,当然一点也不简单。本月早些时候 v3 已经通过了 100% 的 CI,他们先求正确,接下来要”既正确又快”。团队打算在接下来几周公开分享性能基准,在达到并超越现有性能之前不会上线生产。文末介绍 turbopuffer 目前托管超过 1 万亿文档、每秒处理 1000 万次以上写入、10 万次以上查询。v3 还会为把更多 SQL 查询(如 GROUP BY 和聚合)搬到 turbopuffer 上打下基础。

HN 评论精华

帖子 403 分、约 120 条评论。讨论有几条主线:数据库老手把这次改动类比为 Postgres 与 MySQL/InnoDB 的经典之争;不少人借机表达”纯向量数据库本来就没必要”;还有一段关于标题党和”这是不是 AI 写的”的插曲,以及一段相当跑题的关于 HN 上 LLM 水军的长讨论。

Postgres vs InnoDB,十年后的重演。 blakeashleyjr 说这听起来就像晚了十年的 Postgres 与 InnoDB 之争:倒排指向的是物理位置(ANN 槽位),所以每次 SPFresh 重平衡都要重写所有涉及该文档的索引。InnoDB 的解法是让二级索引指向主键,读的时候多查一次。”更新一个向量会移动几百个属性和索引”基本就是 Uber 2016 年那篇 Postgres 写放大文章的搜索版,解法也一样:别让索引指向行所在的位置。他好奇的是,当这次额外查找是一次 S3 GET 而不是一次 B 树跳转时代价如何,簇里是否还会保留向量副本让检索本身保持局部性,否则冷启动的 p99 可能变差。gopalv 也提出相同类比:turbopuffer 的设计从 Postgres 模式转向了 MySQL 模式,Postgres 的索引指向每次更新都会变的行 ID,优化的是查找;MySQL 指向主键索引条目,多一层间接,但只要主键不变就不必更新所有属性索引。他还半开玩笑地说 Postgres 更适合良好范式设计、MySQL 是为”糟糕设计”优化的。phoghed 回了一句”今天才知道我应该一直用 MySQL”,woadwarrior01 则联想到 Richard Gabriel 的”更差就是更好”。

malisper 不同意”Postgres 更适合好设计”的说法:二级索引指向主键可以支持 undo 日志,从而省去 vacuum,而 vacuum 是 Postgres 最痛苦的部分;何况主键索引大多在缓存里,多一次内存中的 B 树遍历比读磁盘便宜几个数量级。mattashii 补充说堆表也可以有 undo 日志,两者并无必然联系。SigmundA 提到 MSSQL 的聚簇索引和 Oracle 的索引组织表允许用户自选,他从 MSSQL 转过来后很怀念这个能力。

有人问新的主索引是什么,turbopuffer 的 benesch 回答:是一个自动生成的内部 ID(段 ID + 文档 ID),用户提供的 id 字段在存储层反而变成一个二级索引。

“根本不需要纯向量数据库”。 sreekanth850 的评论得到不少附和:他们在一个原生支持向量的 SQL 数据库(CrateDB)上构建了企业检索引擎,向量相似度只是和全文检索、过滤、连接、排序并列的一种查询原语,租户隔离、ACL、文档版本、时间过滤都只是普通谓词;再加一个向量数据库意味着多一个组件,而两套系统之间的数据同步”是最难做对的事”。_kidlike 说他们出于同样理由用 Elasticsearch 做向量检索。peterpanhead 多次推荐 MariaDB。Tsarp 提到 LanceDB 早就把 ANN 当二级索引,行存在 fragment 里,向量索引从不移动它们;TopK 的 marekgalovic 也出来说他们很早就意识到这个问题。pjmlp 说他有种 NoSQL 时代的既视感,宁可继续用经典 SQL 数据库,等尘埃落定后它们会补上真正重要的功能。drpython 则从根上质疑 RAG 和向量数据库:为了解决一个已经很难的问题又制造了新问题,还让人为已经付过费的存储和检索再付一次钱。

标题党与”AI 写的”之争。 akras14 指出:他们并没有杀死向量数据库或向量检索,杀死的是”以向量为主的存储布局”,文章自己也说 v3 会让向量检索更快,”RIP,向量主索引”才准确,”RIP,向量数据库”是营销。gk1 则说向量数据库从来都更关乎检索,只是这个名字粘得太牢。vhiremath4 一句”AI 垃圾,不读”引来反驳,gravitronic 说 turbopuffer 的创始人是他共事过的最聪明的人之一,peregrine(turbopuffer 方)确认”我们还是手写的”。croemer 和 nightfly 仍觉得某些句式像 LLM,mediaman 则说这种句式和破折号一样是人类一直在用的写法。

其他。 croemer 发现文中链接的 v3 进度看板很久没更新,benesch 解释图表会等配套的开发日志写好后再更新,团队”沉迷性能调优,很难抽身去写日志”。childintime 提出一个激进设想:是否该让 LLM 为每个场景直接生成定制的 Rust 实现来取代通用数据库?大多数人反对(dymk:”该把锤子换成瑞士军刀了吗?”),但 eatonphil 说他已经在两家公司见到有人这样做,自己也在做,尤其是不存在数据丢失风险的搜索索引。开头还有一段关于页面加载慢的口水仗,以及由此引出的 HN 上 LLM 评论泛滥、是否该学 Lobsters 实行邀请制的讨论,与文章本身关系不大。