Timescale 开源 pg_vectorsearch:PRISM 索引号称快于 pgvector 的 IVFFlat 和 HNSW
Pg_vectorsearch: An index that is faster than pgvector's IVFFlat and HNSW
Timescale 发布 PostgreSQL 扩展 pg_vectorsearch,提供 PRISM 索引用于高性能近似最近邻搜索,官方称在查询和索引构建上都快于 pgvector 的 IVFFlat 与 HNSW,在 1 亿向量数据集上以四分之一内存达到同等召回率。
一个用于高性能近似最近邻(ANN)搜索的 PostgreSQL 扩展。
状态:alpha。仅供测试,不适用于生产环境。预发布版本之间可能无法升级;请计划删除并重建扩展及其索引。已知问题请参阅 changelog。
- PRISM 索引(1 亿+ 向量)——IVF 风格,带分层路由
- RaBitQ 量化,具有两阶段搜索的理论误差界
- SIMD 优化的距离计算(AVX2、AVX512、NEON)
- 动态更新——插入、更新和删除,通过
prism_rebalance进行 posting-list 拆分(参见 Maintenance) - pgvector 兼容——可直接为现有的
vector和halfvec列建立索引(参见 Existing pgvector tables)
PRISM 索引
PRISM(Partitioned Routing Index for Similarity Matching,用于相似度匹配的分区路由索引)是一种 PostgreSQL 原生的索引访问方法,概念上类似于 pgvector 的 IVFFlat,但它围绕最先进的量化、路由和分区技术构建。它用 C 实现,以实现高性能并与 PostgreSQL 无缝集成。
结果是:一个在查询和索引构建方面都显著快于 pgvector 的 IVFFlat 和 HNSW 的索引。事实上,与基于图的 HNSW 相比,在 1 亿向量的数据集上,PRISM 在同等召回率下更快,而所用实例的内存只有其四分之一。
为实现这一性能,PRISM 使用分层质心树将向量划分到簇中,并依赖 PostgreSQL 缓冲区缓存使该树常驻内存。向量经过 RaBitQ 量化后存储在 posting list 中,其页面布局针对 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。
用法
先安装扩展;参见 Building。
使用 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 list,因此列表会随着行的到来而增长,并且永远不会自行分裂。除了 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 list
- 架构 - 高层设计和数据结构
- 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
