Pg_vectorsearch:PostgreSQL 上比 pgvector HNSW/IVFFlat 更快的 PRISM 向量索引
Pg_vectorsearch: An index that is faster than pgvector's IVFFlat and HNSW
Timescale 开源 PostgreSQL 扩展 pg_vectorsearch,引入 PRISM 索引,在 1 亿向量规模下以四分之一内存达成比 pgvector HNSW 更快的同等 recall 速度。
介绍 PRISM 索引的实现思路,它在 1 亿向量规模下比 pgvector HNSW 更快,并附上安装、查询与现有 HNSW 并存替换的完整示例。
PostgreSQL 扩展,用于高性能近似最近邻 (ANN) 搜索。
状态:alpha。 仅供测试,不用于生产。预发布版本之间可能无法升级;请计划删除并重建扩展及其索引。有关已知问题,请参阅 更新日志。
- PRISM 索引(1 亿+ 向量)— 类 IVF,采用分层路由
- RaBitQ 量化,具有理论误差界,用于两阶段搜索
- SIMD 优化的距离计算(AVX2、AVX512、NEON)
- 动态更新— 插入、更新和删除,通过
prism_rebalance进行倒排列表拆分(参见 维护) - 兼容 pgvector— 直接为现有的
vector和halfvec列建立索引(参见 现有的 pgvector 表)
PRISM 索引
PRISM(Partitioned Routing Index for Similarity Matching,相似性匹配分区路由索引)是一种 PostgreSQL 原生索引访问方法,在概念上与 pgvector 的 IVFFlat 类似,但它围绕最先进的量化、路由和分区技术构建。它以 C 语言实现,以获得高性能并与 PostgreSQL 无缝集成。
结果:无论查询还是构建索引,索引速度显著快于 pgvector 的 IVFFlat 和 HNSW。事实上,与基于图的 HNSW 相比,在内存仅为其四分之一的 1 亿向量数据集上,PRISM 在相同召回率下速度更快。
为实现这一性能,PRISM 使用分层质心树将向量划分为簇,并依赖 PostgreSQL 缓冲区缓存使该树在内存中保持热度。向量经过 RaBitQ 量化,并存储在针对 SIMD 距离计算优化页面布局的倒排列表中。RaBitQ 编码携带误差界,使搜索可以排除不可能是最近邻的向量,因此需要根据表中存储的全精度向量进行重排序的向量要少得多。
Query Flow:
1. Traverse centroid tree to find nearest clusters (buffer cache)
2. Scan posting lists for candidates (buffer cache, async I/O)
3. RaBitQ two-stage filtering: estimate → error bound → rerank
4. Full-precision reranking from heap
5. Return k nearest neighbors
有关详细架构,请参阅 docs/architecture.md。
用法
请先安装扩展;参见 构建。
使用 PRISM 索引有两种方式。选择与你的向量存储位置相匹配的方式:
- 原生类型。 从头开始,或者不使用 pgvector?将向量存储在
pg_vectorsearch自有的vec32(float32) 或vec16(float16) 列中。无需其他扩展。 - 现有的 pgvector 表。 已有
vector或halfvec列?在 pgvector 旁边安装pg_vectorsearch,并按原样在这些列上构建 PRISM 索引。你的表和查询保持不变。
两种方式构建相同的索引,并使用相同的操作符(<->、<#>、<=>)和 调优 参数。
原生类型
CREATE EXTENSION pg_vectorsearch; -- PLAIN keeps the vector inline. EXTERNAL (the default) toasts a value -- over ~2 kB, and rerank then pays an extra fetch the planner does not cost. CREATE TABLE items ( id serial PRIMARY KEY, embedding vec32(3) STORAGE PLAIN ); -- Load first; the index is sized from the row count at CREATE INDEX. -- 500 rows is enough for the planner to use the index. INSERT INTO items (embedding) SELECT ARRAY[i::real, (i % 7)::real, (i % 5)::real]::vec32 FROM generate_series(1, 1000) i; -- For inner product or cosine, see Operator classes below. CREATE INDEX ON items USING prism (embedding vec32_l2_ops); -- 0 = auto (~0.95 recall). Raise for recall, lower for speed. 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 中也是如此。
-- The table you already have, included so the example runs on its own. 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); -- Install pg_vectorsearch next to pgvector and build the index on the -- existing column. The column is not rewritten or copied. CREATE EXTENSION pg_vectorsearch; CREATE INDEX documents_embedding_prism ON documents USING prism (embedding vec32_l2_ops); -- Your queries keep working as before. PRISM is normally faster, so -- the planner should prefer it over the HNSW index while both exist. -- Verify with an 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) -- Once you are happy with it, drop the HNSW index. 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、其权衡以及何时更改它们是合适的,请参阅 调优指南。
维护
插入操作会将向量追加到其路由到的 posting 列表中,因此列表会随着行的到来而增长,但不会自行拆分。除了 REINDEX 外,prism_rebalance 过程会就地拆分过大的列表。有关其工作原理和当前限制,请参阅 维护指南。
构建
要求:
- PostgreSQL 18+(带有开发头文件)
- GNU C11 编译器(GCC 11+ 或 Clang 14+)
- Meson 构建系统
- 可选:CBLAS,用于更快的 RaBitQ 编码。请参阅 开发。
# Release build meson setup builddir --buildtype=release # Build and install to PostgreSQL meson install -C builddir
有关构建选项,请参阅 开发指南。
文档
- 调优 - 索引参数、GUC、权衡、默认值
- 维护 - 重新平衡和拆分 posting 列表
- 架构 - 高级设计和数据结构
- SIMD - SIMD 构建选项和距离计算
- 开发 - 构建选项、测试、CI 脚本
参考
研究
- RaBitQ - 具有理论误差界限的二进制量化(SIGMOD 2024)
- SPFresh - 用于增量更新的 LIRE 协议(SOSP 2023)
- SPANN - 基于边界复制的十亿级 ANN(NeurIPS 2021)
- ScaNN for AlloyDB - 层次聚类(Google)
相关项目
- pgvector - PostgreSQL 的向量类型和运算符
- pgvectorscale - 基于 DiskANN 的 PostgreSQL 向量索引
许可证
来源:Hacker News · github.com
