跳到正文
原文
Hacker News· jascha_eng·· 2 小时前精选AI 评分49

Pg_vectorsearch:PostgreSQL 上比 pgvector HNSW/IVFFlat 更快的 PRISM 向量索引

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

AI 导读

Timescale 开源 PostgreSQL 扩展 pg_vectorsearch,引入 PRISM 索引,在 1 亿向量规模下以四分之一内存达成比 pgvector HNSW 更快的同等 recall 速度。

推荐理由

介绍 PRISM 索引的实现思路,它在 1 亿向量规模下比 pgvector HNSW 更快,并附上安装、查询与现有 HNSW 并存替换的完整示例。

正文 · AI 翻译

Build Coverage Coverity

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

状态:alpha。 仅供测试,不用于生产。预发布版本之间可能无法升级;请计划删除并重建扩展及其索引。有关已知问题,请参阅 更新日志。

  • PRISM 索引(1 亿+ 向量)— 类 IVF,采用分层路由
  • RaBitQ 量化,具有理论误差界,用于两阶段搜索
  • SIMD 优化的距离计算(AVX2、AVX512、NEON)
  • 动态更新— 插入、更新和删除,通过 prism_rebalance 进行倒排列表拆分(参见 维护)
  • 兼容 pgvector— 直接为现有的 vector 和 halfvec 列建立索引(参见 现有的 pgvector 表)

PRISM 索引

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 向量索引

许可证

PostgreSQL 许可证

来源:Hacker News · github.com