Hacker News 中文摘要

RSS订阅

Pg_vectorsearch: An index that is faster than pgvector's IVFFlat and HNSW

pg_vectorsearch 是一个用于 PostgreSQL 的高性能近似最近邻(approximate nearest neighbor,ANN)搜索扩展,目前处于 alpha 状态,仅用于测试而非生产环境;预发布版本之间可能无法直接升级,需删除并重建该扩展及其索引,已知问题见 changelog。它提供 PRISM 索引(PRISM index),采用 IVF 风格并支持分层路由,可处理 1 亿以上向量;同时还引入 RaBitQ 量化(RaBitQ quantization),为两阶段搜索提供理论误差边界。

正文

一个用于高性能近似最近邻(ANN)搜索的 PostgreSQL 扩展。

状态:alpha。 仅供测试,勿用于生产。预发布版本之间可能无法升级;请做好删除并重建扩展及其索引的准备。已知问题详见更新日志。

Image 2: Coverage

  • PRISM 索引(支持 1 亿以上向量)——IVF 风格,带分层路由
  • RaBitQ 量化——为两阶段搜索提供理论误差界限
  • SIMD 优化的距离计算(AVX2、AVX512、NEON)
  • 动态更新——支持插入、更新和删除,可通过 prism_rebalance 拆分倒排列表(参见维护)
  • 兼容 pgvector——可直接为已有的 vector 和 halfvec 列建索引(参见已有的 pgvector 表)

PRISM 索引

Image 3: Coverity

PRISM(Partitioned Routing Index for Similarity Matching,面向相似度匹配的分区路由索引)是一种 PostgreSQL 原生的索引访问方法,概念上类似于 pgvector 的 IVFFlat,但它围绕最先进的量化、路由和分区技术构建。它用 C 实现,以获得高性能并与 PostgreSQL 无缝集成。

结果是:无论查询还是建索引,它都比 pgvector 的 IVFFlat 和 HNSW 快得多。事实上,在 1 亿向量的数据集上,与基于图的 HNSW 相比,PRISM 在同等召回率下速度更快,而且是在内存仅为 HNSW 四分之一的实例上达成的。

为了实现这一性能,PRISM 使用分层质心树将向量划分为簇,并依赖 PostgreSQL 的缓冲缓存将该树保留在内存中保持热度。向量经过 RaBitQ 量化后存入倒排列表,页面布局针对 SIMD 距离计算做了优化。RaBitQ 编码带有误差界限,使搜索能排除那些不可能是最近邻的向量,因此需要对表中存储的全精度向量做重排序(rerank)的数量大大减少。

查询流程:

  1. 遍历质心树,找到最近的簇(缓冲缓存)
  2. 扫描倒排列表获取候选向量(缓冲缓存,异步 I/O)
  3. RaBitQ 两阶段过滤:估计 → 误差界限 → 重排序
  4. 从堆中进行全精度重排序
  5. 返回 k 个最近邻

详细架构参见 docs/architecture.md。

使用方法

请先安装扩展;参见构建。

有两种使用 PRISM 索引的方式,请选择与你的向量存储情况相匹配的一种:

  • 原生类型。 如果从头开始,或者没有使用 pgvector?可以把向量存入 pg_vectorsearch 自带的 vec32(float32)或 vec16(float16)列。不需要其他扩展。
  • 已有的 pgvector 表。 如果已经有 vector 或 halfvec 列?在 pgvector 之外再安装 pg_vectorsearch,并直接在这些列上建 PRISM 索引。你的表和查询都无需改动。

两种方式建的是同一种索引,使用相同的操作符(<->、<#>、<=>)和相同的调优参数。

原生类型

CREATE EXTENSION pg_vectorsearch;

-- PLAIN 表示向量内联存储。EXTERNAL(默认)会在值超过约 2 kB 时进行 TOAST 存储,之后重排序要多一次 planner 未计入开销的读取。
CREATE TABLE items (
    id serial PRIMARY KEY,
    embedding vec32(3) STORAGE PLAIN
);

-- 先导入数据;索引大小在 CREATE INDEX 时按行数确定。
-- 500 行就足以让 planner 使用该索引。
INSERT INTO items (embedding)
SELECT ARRAY[i::real, (i % 7)::real, (i % 5)::real]::vec32
FROM generate_series(1, 1000) i;

-- 内积或余弦距离请参见下面的操作符类。
CREATE INDEX ON items USING prism (embedding vec32_l2_ops);

-- 0 表示自动(约 0.95 召回率)。调高可提升召回率,调低可提速。
SET prism.nprobe = 40;

SELECT * FROM items
ORDER BY embedding <-> '[3,1,2]'
LIMIT 5;

如果改用 vec16 代替 vec32,向量在表中占用的空间可减半,并搭配对应的 vec16_*操作符类。

已有的 pgvector 表

如果你已经有含 pgvector vector 或 halfvec 列的表,可以直接在其上建 PRISM 索引。表和查询都无需任何改动。vec32 和 vec16 与 pgvector 的 vector 和 halfvec 二进制兼容,只是名称不同,因此两个扩展可以并排安装,甚至在相同 schema 下共存。

-- 这是你已有的表,为了让示例能独立运行而包含在此。
CREATE EXTENSION vector;

CREATE TABLE documents (
    id        bigserial PRIMARY KEY,
    body      text,
    embedding vector(384)
);
CREATE INDEX documents_embedding_hnsw
    ON documents USING hnsw (embedding vector_l2_ops);

-- 在 pgvector 之外安装 pg_vectorsearch,并在已有列上建索引。
-- 该列不会被重写或复制。
CREATE EXTENSION pg_vectorsearch;

CREATE INDEX documents_embedding_prism
    ON documents USING prism (embedding vec32_l2_ops);

-- 你的查询照旧可用。PRISM 通常更快,因此在两个索引同时存在时,
-- planner 应优先选择它而不是 HNSW 索引。可用 EXPLAIN 验证:
EXPLAIN (COSTS OFF)
SELECT id, body
FROM documents
ORDER BY embedding <-> (SELECT embedding FROM documents WHERE id = 42)
LIMIT 10;
-- Limit
-- InitPlan 1
-- -> Index Scan using documents_pkey on documents documents_1
-- Index Cond: (id = 42)
-- -> Index Scan using documents_embedding_prism on documents
-- Order By: (embedding <-> (InitPlan 1).col1)

-- 确认无误后,删除 HNSW 索引。
DROP INDEX documents_embedding_hnsw;

使用与你的 HNSW 索引相同距离度量的 PRISM 操作符类。

操作符类

为你的距离度量和列类型选择操作符类。L2 是默认选项。

| 距离度量 | 操作符 | vec32 / vector | vec16 / halfvec | | --- | --- | --- | --- | | L2 | <-> | vec32_l2_ops | vec16_l2_ops | | 内积 | <#> | vec32_ip_ops | vec16_ip_ops | | 余弦 | <=> | vec32_cosine_ops | vec16_cosine_ops |

替换 HNSW 索引时,pgvector 的 vector_<metric>_ops 对应 vec32_<metric>_ops,halfvec_<metric>_ops 对应 vec16_<metric>_ops。

调优

默认值基于大规模基准测试调优;大多数部署只需调整 prism.nprobe。所有索引参数、GUC、它们的权衡以及何时适合修改,参见调优指南。

维护

插入操作会把向量追加到其路由到的倒排列表中,因此列表会随数据写入而增长,且不会自动分裂。除了 REINDEX,prism_rebalance 过程可就地拆分过大的列表。其工作原理及当前限制参见维护指南。

构建

要求:

  • PostgreSQL 18+(含开发头文件)
  • GNU C11 编译器(GCC 11+ 或 Clang 14+)
  • Meson 构建系统
  • 可选:CBLAS,用于加速 RaBitQ 编码。参见开发。

    发布构建

    meson setup builddir --buildtype=release

    构建并安装到 PostgreSQL

    meson install -C builddir

构建选项参见开发指南。

文档

  • 调优 —— 索引参数、GUC、权衡与默认值
  • 维护 —— 重新平衡与拆分倒排列表
  • 架构 —— 高层设计与数据结构
  • SIMD —— SIMD 构建选项与距离计算
  • 开发 —— 构建选项、测试、CI 脚本

参考

研究

  • RaBitQ —— 带理论误差界限的二值量化(SIGMOD 2024)
  • SPFresh —— 用于增量更新的 LIRE 协议(SOSP 2023)
  • SPANN —— 采用边界复制的十亿级 ANN(NeurIPS 2021)
  • ScaNN for AlloyDB —— 分层聚类(Google)

相关项目

  • pgvector —— PostgreSQL 的向量类型与操作符
  • pgvectorscale —— 基于 DiskANN 的 PostgreSQL 向量索引

许可证

PostgreSQL 许可证

评论

讨论开场被官方“更快、更省内存”的 PRISM 索引宣传吸引,但实际气氛很快转为审视落地条件。少数人为低内存向量索引与竞争利好叫好,较多人则质疑过滤、托管 PostgreSQL 支持、PostgreSQL 18 要求、维度上限和基准测试缺失等现实门槛;分歧主轴是性能叙事与生产可用性之间的落差。

看好低内存向量索引的竞争与降本潜力

  • 正在使用 VectorChord 的开发者表示,配合高速 NVMe 磁盘已把 Postgres 成本降低一半,因此欢迎更多低 RAM 向量索引工具入局;内存涨价带来的压力正促使社区利用现代 NVMe 的速度。(少数人) > 我正用 VectorChord,配合高速磁盘把 Postgres 成本砍半,很高兴看到低 RAM 向量索引工具出现竞争。(@EgregiousCube)

指出托管 PostgreSQL 与版本升级要求阻碍采用

  • 如果云厂商不在托管 PostgreSQL 中提供该扩展,依赖托管服务的团队基本无法使用;它又要求 PostgreSQL 18、仍属刚发布的 alpha,未进入 Azure 等超大规模云服务商托管 PostgreSQL 的允许扩展列表,也尚未在 Timescale 之外的生产环境经受检验,旧版自建迁移成本过高,短期难以落地。(较多人)

质疑过滤、租户隔离与维度上限等能力缺口

  • 过滤目前只支持后过滤,有人质疑这会让租户、用户或权限范围隔离难以高效实现;同时 PRISM 的向量维度上限为 1968,原有更高维的 HNSW 索引暂时无法直接替换。(较多人)

认为应先公开 QPS 与召回率等严谨基准

  • 有人质疑官方把性能结论讲得太像营销,认为如果真快,就应该首先给出 QPS 与精度/召回率,而不是只展示延迟和内存节省。(少数人) > 既然主打快,就该先把 QPS 和精度/召回率放出来。(@esafak)

追问与 pgvectorscale、pg_search、RoarGraph 等方案的差异

  • 多位评论者接连把它与 pgvectorscale、ParadeDB 的 pg_search、RoarGraph 等现有向量或搜索方案放在一起比较,想先确认定位与替代关系,而不是只听单一宣传。(较多人)

有价值的补充

  • 仓库封面图被指完全致敬 Pink Floyd 的《The Dark Side of the Moon》,索引名 PRISM 也被玩成了「Dark Side of the PRISM」的谐音梗。