跳到正文
Hacker News· jascha_eng·· 3 天前AI 评分58

Timescale 开源 pg_vectorsearch:PRISM 索引号称快于 pgvector 的 IVFFlat 和 HNSW

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

AI 导读

Timescale 发布 PostgreSQL 扩展 pg_vectorsearch,提供 PRISM 索引用于高性能近似最近邻搜索,官方称在查询和索引构建上都快于 pgvector 的 IVFFlat 与 HNSW,在 1 亿向量数据集上以四分之一内存达到同等召回率。

正文 · AI 翻译

Build Coverage Coverity

一个用于高性能近似最近邻(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

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

许可证

The PostgreSQL License

来源:Hacker News · github.com