pgvector 基准测试为何会骗人

pgvector 是一个开源 Postgres 扩展,让你能在关系型数据旁边存储和查询向量嵌入,使用你已有的表、事务和工具链。去年十月,Alex Jacobs 发表了一篇名为《反对 pgvector 的理由》的文章,在工程圈里流传甚广。他的观点是,那些鼓吹 pgvector 能直接替代专用向量数据库的博客文章,都忽略了在生产环境中大规模运行它的现实挑战(这里不展开,但文章确实值得一读)。
他说的没错。当时他描述的大部分情况对于原版 pgvector 都是准确的,这凸显了博客承诺与团队从本地 Postgres 实例转向生产工作负载时所遇到情况之间的差距。但过去几个月,pgvector 的故事已经进化了不少。
v0.5.0 引入的 HNSW 索引,相比 IVFFlat 基于聚类的方法,提升了召回率和查询一致性。增量索引构建能力更强了,在托管环境中运行 pgvector 的团队也开发出应对 Jacobs 描述的许多故障模式的操作模式。
这篇文章讲的是在你决定尝试 pgvector 后,如何成功使用它。Jacobs 的观点恰当地描述了团队把 pgvector 当作即插即用技术时会遇到什么,而接下来要说的就是如何避免那种结果。
笔记本 vs. 生产环境
pgvector 出现“演示能用,生产就崩”的模式,很可能是因为规模变化改变了哪些问题重要。在 1 万条 128 维向量上跑基准测试,结果很漂亮,查询和索引构建都很快。但这个基准测试几乎无法告诉你,同样的设置在 500 万条 1536 维向量下会如何运行。在那个规模下,索引本身就成了基础设施问题。
跨越数百万高维向量的 HNSW 索引,构建时所需的内存远多于查询时,而且这些内存来自你的生产数据库。索引构建可能持续数小时,过滤向量查询的查询规划器成本估算可能波动很大,而且任何部署(或故障转移)后,首批用户都要承受冷缓存惩罚,因为 ANN 算法需要时间进入状态。
“在 1 万条 128 维向量上跑基准测试,结果很漂亮……但这个基准测试几乎无法告诉你,同样的设置在 500 万条 1536 维向量下会如何运行。”
把这些看作有已知解决方案的工程问题,而不是障碍。没错,它们需要不同于标准关系型 SQL 的操作思维,而且可能只在工作负载达到一定规模时才显现。但措手不及的团队,往往是那些用非代表性规模数据做基准测试的。
先做基准测试再决定!
我坚信这是 pgvector 实施中最被忽视的一步。跳过它风险自负,因为它可能比其他任何失误带来更多痛苦。
社区基准测试能给你一个大致的预期,但在你实际应用中的性能会因向量维度、数据分布和数据集大小而异(通常差异显著)。在 1 万条 128 维向量上跑的基准测试,几乎无法告诉你你的系统在 500 万条 1536 维向量下会如何表现。
所以,在确定索引类型或数据库配置前,先用代表你实际数据集的数据跑自己的基准测试。在你预期达到的工作负载规模下,测量查询延迟、索引构建时间和搜索召回率。现在花一小时做基准测试,能让你免于日后更痛苦、更漫长的重构。
选择并调优索引策略
IVFFlat 与 HNSW 的选择取决于工作负载的匹配度。先说 IVFFlat。它构建更快,产生的索引更紧凑,是周期性批量更新或相对适中数据集的可靠选择。通过调整 lists(创建多少分区)和 probes(每次查询扫描多少分区)来控制速度/召回率的权衡。但关键要注意,IVFFlat 索引需要训练数据来创建有效的分区(这应该在数据加载后构建,而不是之前)。
然而,当你需要低查询延迟和高召回率来应对频繁的向量查询时,HNSW 胜出。它的图结构支持更快遍历,但索引创建时间更长,占用内存更多。这里的关键参数是 ef_search(算法在查询期间探索的广度)和 M(每个节点维护的连接数)。
无论你选哪个,都要针对你实际的查询模式和召回目标对这些参数进行基准测试。默认值和优化值之间的差距可能很大……然后,当你确定了有效的参数值后,把这些设置和索引定义一起存储起来。六个月后你的团队更新嵌入模型时,向量的维度和分布会变化,所以调优参数也需要相应调整。
为混合检索设计
在 Postgres 内运行向量搜索的一个未被充分利用的能力,是将其与结构化 SQL 操作结合。太多团队把 pgvector 当作一个碰巧在 Postgres 里的独立向量存储。这样做的人错失了显著的性能提升。
与其在整个数据集上运行向量相似性搜索,不如先用 SQL WHERE 子句缩小候选集。你可以按租户 ID、语言、内容类型或日期范围过滤。然后让 ANN 索引在这个缩小的集合上工作,而不是扫描整个表。特别是在多租户应用中,这种方法通常能将查询性能提升一个数量级。
你甚至可以更进一步,采用两阶段检索管道。先运行快速的 ANN 查询,获取前 N 个候选。然后用精确距离计算结合业务逻辑(新鲜度、用户权限、流行度加权等)对这些候选进行重排序。通过在 SQL 中进行重排序,你可以将整个操作保持在单个事务内。
这种混合方法是 pgvector 与 Postgres 集成带来最大回报的地方之一。虽然专用向量数据库擅长相似性搜索,但将其与任意 SQL 过滤器和事务性业务逻辑结合,通常需要编排层。但有了开源的 pgvector,你只需写 SQL。
智能分区,主动预热
你构建表的方式会影响向量查询性能,任何纯粹按数据量分区的本能都可能错过重点。
目标是按与你实际查询过滤器相关的字段分区。所以,如果你的应用总是按租户过滤,那就按租户分区。然后为每个分区构建向量索引,这样查询规划器可以在规划时修剪整个分区,意味着对于任何给定查询,向量索引只覆盖你总数据集的一小部分。
“任何纯粹按数据量分区的本能都可能错过重点。目标是按与你实际查询过滤器相关的字段分区。”
另一个在生产中让团队措手不及的是冷缓存性能。部署或故障转移后,支撑你向量索引的页面不会在内存中。首批访问系统的用户将承担从磁盘加载这些页面的成本,同时 ANN 算法遍历图。这时可以用像 pg_prewarm 这样的工具,让你在流量到来之前将热页面加载到共享缓冲区。你可以把这构建到部署流程中,这样从部署到服务的过渡就不会降低性能。
了解其边界
每个工具都有局限性,pgvector 也不例外。关键是理解它们。pgvector 正在积极开发中,版本兼容性是一个考虑因素,因为该工具支持某些 Postgres 版本但不支持其他。扩展需要你手动调优,就像应对任何 Postgres 性能挑战一样,没有自动调优层来处理内存分配、查询优化或索引配置。
对于需要在数千万向量上实现亚 20 毫秒延迟的应用,pgvector 可能是一个强大的起点,最终会升级到专用解决方案。从这里开始可以让你验证用例并理解查询模式(无需为单独的基础设施投入大量前期成本)。即使你超出了它的能力范围,你也会带着对你实际需求的更深入了解进行迁移。
成功团队的区别
一个贯穿始终的线索是,那些从 pgvector 中获得最大收益的团队,都像对待任何其他严肃的 Postgres 工作负载一样对待它。他们在做出重大架构决策前,会用代表性数据进行基准测试,并有意识地调优索引参数,而不是盲目接受默认值。他们还设计能充分利用完整 SQL 工具包的查询,并清楚了解 pgvector 在哪里适用,在哪里不适用。
如果你的团队已经在运行 Postgres 并且需要向量搜索,pgvector 从问题中移除了大量的架构复杂性。关键在于投入操作努力来良好运行它。Jacobs 说得对,博客文章跳过了困难的部分……但困难的部分通过正确的操作方法是可以管理的。
觉得有用?分享给更多人