Pg_vectorsearch: An index that is faster than pgvector's IVFFlat and HNSW
https://github.com/timescale/pg_vectorsearch📌 PostgreSQL 新索引 PRISM:號稱同時打敗 IVFFlat 與 HNSW
TL;DR:Timescale 開源 pg_vectorsearch,其 PRISM 索引宣稱在億級向量規模下比 HNSW 更快、更省記憶體。
向量資料庫的標準答案長期是 pgvector 的 HNSW 或 IVFFlat,但如果有一種索引同時比兩者都快,還能用更少的記憶體撐住一億筆向量,會是怎麼做到的?這就是 pg_vectorsearch 這個新 PostgreSQL extension 想回答的問題。
🤔 解決什麼問題
pg_vectorsearch 是一個用於高效能 approximate nearest neighbor (ANN) 搜尋的 PostgreSQL extension,目前狀態為 alpha,官方明確標示僅供測試、尚不建議用於生產環境,且 prereleases 之間可能無法直接升級,需要砍掉重建 extension 與索引。核心賣點是它的 PRISM 索引(Partitioned Routing Index for Similarity Matching):概念上類似 pgvector 的 IVFFlat,但用了更先進的量化、路由與分區技術,以 C 語言實作。官方宣稱在一億筆向量的資料集上,PRISM 不只比 IVFFlat 與 HNSW 快,在同等 recall 下,相較圖結構的 HNSW,PRISM 使用的記憶體僅為四分之一。
🧩 核心架構:分層路由 + RaBitQ 量化 + SIMD
PRISM 用一個 hierarchical centroid tree 將向量切分到不同 cluster,並依賴 PostgreSQL 的 buffer cache 讓這棵樹保持在記憶體中。向量本身經過 RaBitQ 量化後存入 posting lists,頁面佈局針對 SIMD 距離計算最佳化(支援 AVX2、AVX512、NEON)。RaBitQ 編碼帶有理論誤差界(error bounds),讓搜尋可以直接排除「不可能是最近鄰」的向量,大幅減少需要對全精度向量做 rerank 的數量。
一次查詢的流程大致是:
- 走訪 centroid tree 找出最接近的 cluster(走 buffer cache)
- 掃描 posting lists 取得候選向量(buffer cache + 非同步 I/O)
- RaBitQ 兩階段過濾:估計距離 → 套用誤差界 → 篩選進入 rerank
- 從 heap 取出全精度向量做最終 rerank
- 回傳 k 個最近鄰
索引支援動態更新(insert、update、delete),透過 prism_rebalance 程序對過大的 posting list 做分裂。插入時向量會直接附加到對應的 posting list,list 本身不會自動分裂,所以除了 REINDEX 外,維護時需要依賴這個 rebalance 程序。
🧪 怎麼用:原生型別,或直接插在既有 pgvector 表上
pg_vectorsearch 提供兩條使用路徑。第一種是原生型別:新專案或還沒用 pgvector 的話,可以直接用 vec32(float32)或 vec16(float16)欄位型別,不需要安裝其他 extension。第二種是相容既有 pgvector 表:如果資料表已經有 vector 或 halfvec 欄位,可以把 pg_vectorsearch 裝在 pgvector 旁邊,直接在原欄位上建 PRISM 索引,資料表與查詢語法完全不用改。因為 vec32/vec16 與 pgvector 的 vector/halfvec 二進位相容,兩套 extension 可以並存在同一個 schema 裡。
兩條路徑都使用相同的運算子(<->、<#>、<=>)與調參選項,對應 L2、inner product、cosine 三種距離。大多數部署情境下,官方建議唯一需要調的參數是 prism.nprobe,預設 0 代表自動達到約 0.95 recall,調高換 recall、調低換速度。
⚠️ 適用場景與限制
建置需求是 PostgreSQL 18+、GNU C11 編譯器(GCC 11+ 或 Clang 14+)與 Meson build system,CBLAS 則是加速 RaBitQ 編碼的可選依賴。因為官方明確定調為 alpha,目前更適合用在評估與測試環境,而非直接上生產。想從 HNSW 遷移的團隊,可以先在既有表上並存建 PRISM 索引,用 EXPLAIN 驗證查詢計畫是否改走 PRISM,確認無虞後再移除舊的 HNSW 索引——這也是 README 示範的遷移方式。
🎯 實務啟示
如果你的向量檢索規模正在往億級邁進,又受夠了 HNSW 建索引的記憶體開銷,pg_vectorsearch 提供了一條「不用改 schema、不用改查詢」的漸進式評估路徑:先裝在既有 pgvector 表旁邊跑基準測試,再決定是否全面切換。但鑑於 alpha 狀態與升級路徑的限制,建議先在非關鍵資料集上驗證穩定性。
🔗 來源
- 標題:Pg_vectorsearch: An index that is faster than pgvector’s IVFFlat and HNSW
- 作者/機構:jascha_eng(Hacker News);專案由 Timescale 維護(GitHub: timescale/pg_vectorsearch)
- 連結:https://github.com/timescale/pg_vectorsearch
#PostgreSQL #VectorSearch #ANN #Timescale #pgvector #RaBitQ #SIMD #DatabaseEngineering #OpenSource #RAG
原始資料 Hacker News · 收集於 2026-10-14
摘要原文
pg_vectorsearch A PostgreSQL extension for high-performance approximate nearest neighbor (ANN) search. Status: alpha. Meant for testing, not production. Upgrading between prereleases may not be possible; plan to drop and recreate the extension and its indexes. See the changelog for known issues. PRISM index (100M+ vectors) — IVF-style, with hierarchical routing RaBitQ quantization with theoretical error bounds for two-stage search SIMD-optimized distance computation (AVX2, AVX512, NEON) Dynamic updates — inserts, updates and deletes, with posting-list splits via prism_rebalance (see Maintenance ) pgvector compatible — index existing vector and halfvec columns as-is (see Existing pgvector tables ) PRISM Index PRISM (Partitioned Routing Index for Similarity Matching) is a PostgreSQL-native index access method that is conceptually similar to pgvector 's IVFFlat, but it is built around state-of-the-art quantization, routing, and partitioning techniques. It is implemented in C for high performance and seamless integration with PostgreSQL. The result: an index that is significantly faster than pgvector's IVFFlat and HNSW, for both querying and index building. In fact, compared to graph-based HNSW, PRISM is faster at equivalent recall on a 100-million-vector dataset, on an instance with a quarter of the memory. To achieve this performance, PRISM uses a hierarchical centroid tree to partition vectors into clusters, and relies on the PostgreSQL buffer cache to keep that tree warm in memory. Vectors are RaBitQ -quantized and stored in posting lists with a page layout optimized for SIMD distance computation. The RaBitQ encoding carries error bounds that let a search rule out vectors that cannot possibly be nearest neighbors, so far fewer of them need reranking against the full-precision vectors stored in the table. 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 For detailed architecture, see docs/architecture.md . Usage Install the extension first; see Building . There are two ways to use a PRISM index. Pick the one that matches where your vectors live: Native types . Starting fresh, or not using pgvector? Store vectors in pg_vectorsearch 's own vec32 (float32) or vec16 (float16) columns. No other extension is needed. Existing pgvector tables . Already have vector or halfvec columns? Install pg_vectorsearch next to pgvector and build a PRISM index on those columns as they are. Your table and queries stay unchanged. Both paths build the same index and use the same operators ( <-> , <#> , <=> ) and tuning knobs. Native types 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 ; Use vec16 instead of vec32 to halve the space vectors take in the table, with the matching vec16_* operator classes . Existing pgvector tables If you already have a table with pgvector's vector or halfvec columns, you can build a PRISM index on it directly. Nothing about the table or your queries needs to change. vec32 and vec16 are binary-compatible with pgvector's vector and halfvec but have different names, so the two extensions can be installed side by side, even in the same 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; Use the PRISM operator class for the same distance metric as your HNSW index. Operator classes Pick the operator class for your distance metric and column type. L2 is the default. Distance Operator vec32 / vector vec16 / halfvec L2 <-> vec32_l2_ops vec16_l2_ops Inner product <#> vec32_ip_ops vec16_ip_ops Cosine <=> vec32_cosine_ops vec16_cosine_ops When replacing an HNSW index, pgvector's vector_<metric>_ops maps to vec32_<metric>_ops and halfvec_<metric>_ops to vec16_<metric>_ops . Tuning Defaults are tuned from large-scale benchmarks; most deployments only ever adjust prism.nprobe . See the tuning guide for every index parameter and GUC, their tradeoffs, and when changing them makes sense. Maintenance An insert appends to whichever posting list its vector routes to, so lists grow as rows arrive and never split on their own. Besides REINDEX , the prism_rebalance procedure splits oversized lists in place. See the maintenance guide for how it works and its current limitations. Building Requirements: PostgreSQL 18+ (with development headers) GNU C11 compiler (GCC 11+ or Clang 14+) Meson build system Optional: CBLAS, for faster RaBitQ encoding. See Development . # Release build meson setup builddir --buildtype=release # Build and install to PostgreSQL meson install -C builddir See the development guide for build options. Documentation Tuning - Index parameters, GUCs, tradeoffs, defaults Maintenance - Rebalancing and splitting posting lists Architecture - High-level design and data structures SIMD - SIMD build options and distance computation Development - Build options, testing, CI scripts References Research RaBitQ - Binary quantization with theoretical error bounds (SIGMOD 2024) SPFresh - LIRE protocol for incremental updates (SOSP 2023) SPANN - Billion-scale ANN with boundary replication (NeurIPS 2021) ScaNN for AlloyDB - Hierarchical clustering (Google) Related Projects pgvector - Vector type and operators for PostgreSQL pgvectorscale - DiskANN-based vector index for PostgreSQL License The PostgreSQL License (173 points, 47 comments on Hacker News)
由 claude-code/sonnet 自動生成