專案 GitHub:timescale/pg_vectorsearch
重點
- PostgreSQL 原生的 IVF 索引:新的 index access method
prism,先把向量分群,查詢時只掃最接近的幾群。群的中心組成一棵階層式 centroid 樹,放在 PostgreSQL 的 buffer cache 裡。 - RaBitQ 量化加誤差上界:每個向量壓成每維 1 bit 的正負號碼,估出的距離附帶誤差上界,確定不可能進前 k 名的候選直接排除,只有少數候選回表用全精度重排。
- 能直接用在 pgvector 欄位:自帶
vec32、vec16型別,與 pgvector 的vector、halfvec二進位相容,既有欄位不必改寫或複製。 - alpha 版本:v0.1.0 於 2026 年 10 月 13 日發布,README 寫明只供測試、不建議上 production,需要 PostgreSQL 18 以上。
- 效能數字都是專案自述:「比 pgvector 的 IVFFlat 和 HNSW 快」來自官方發表文,目前沒有第三方重跑。
以 2026 年 10 月 20 日從 GitHub API 查到的資料,專案有 393 顆星、13 個 fork、46 個開啟中的 issue,授權是 PostgreSQL License。repo 在 2026 年 1 月就已建立,v0.1.0 正式版於 10 月 13 日 21
(台北時間)發布,之前有兩個 release candidate。Hacker News 上的發表串拿到 211 分。內容根據 README、CHANGELOG.md、docs/architecture.md、docs/fastscan-design.md,以及 src/quant/rabitq.h、src/pg/bufstorage.c、src/index/query_scan.c 等原始碼。
它解決什麼問題
在 PostgreSQL 裡做向量搜尋,最常見的是 pgvector 提供的兩種索引:
- IVFFlat:先分群,每群存全精度向量。查詢要逐一算掃到的每個向量的距離,也沒有辦法提早判斷哪些不可能是答案。
- HNSW:圖索引,查詢快、召回率高,但吃記憶體,建索引也慢,資料量上億時成本很高。
PRISM(Partitioned Routing Index for Similarity Matching)走 IVF 路線,architecture.md 自述設計參考 SPANN、SPFresh 和 ScaNN。重點是讓分群裡存的東西變小、讓距離估計可以安全地排除候選。
怎麼運作
README 列出的查詢流程:
- 走 centroid 樹找出最近的幾個群(在 buffer cache 裡)
- 掃這些群的 posting list 取得候選
- RaBitQ 兩階段篩選:估距離、套誤差上界、留下需要重排的
- 回 heap 讀全精度向量重排
- 回傳前 k 個
RaBitQ:1 bit 加誤差上界
src/quant/rabitq.h 的註解寫了演算法:
- 用隨機正交矩陣旋轉向量,讓各維的分布接近等向;
- 算向量減去所屬群中心的殘差;
- 每一維只留正負號,D 維向量壓成 D bit(32 倍壓縮);
- 另外存幾個浮點因子(
f_add、f_rescale、f_error); - 查詢時,估計距離等於
f_add + f_rescale × 內積,下界是估計值減去誤差項。
如果某個候選的距離下界已經比目前第 k 名還遠,它就不可能是答案,不必回表。這是 IVFFlat 做不到的事。RaBitQ 的方法來自 Gao 與 Long 在 SIGMOD 2024 的論文。
Fastscan:一個指令查 32 筆
docs/fastscan-design.md 說明了 posting list 的第二種頁面格式。原本的做法是逐個向量算「查詢向量在 bit 為 1 的那些維度的總和」,768 維就是約 768 次浮點加法。
Fastscan 每 4 維組成一個子量化器,4 個 bit 共有 16 種正負號組合,事先為每個子量化器算一張 16 格的 uint8 查表。1 bit 碼重新打包成 4 bit 一組,用 x86 的 VPSHUFB 一個指令查出 32 個向量的部分距離。768 維時共 96 個 VPSHUFB 處理 32 個向量。
| 設定(cohere-1M、nprobe=10,專案自述) | 原本的 1 bit 核心 | fastscan |
|---|---|---|
| QPS | 5296 | 7789 |
| 召回率 | 0.799 | 0.791 |
| 內積時間 | 136μs | 46μs |
同一條群的頁面鏈裡可以混放兩種頁面,以頁面 flag PRISM_POSTING_PAGE_FASTSCAN 區分。大量建索引和插入時寫一般的 AoS 頁,之後再轉換成 fastscan 頁。
其他元件
- SIMD 分派:距離計算有
distance_avx2.c、distance_avx512.c、distance_neon.c。 - 平行建索引:
parallel_build_leader.c、parallel_build_worker.c。 - posting list 分裂:群變得太大時可以呼叫
prism_rebalance原地分裂(src/index/posting_split.c)。 - 自動 nprobe:
prism.nprobe = 0時由prism_auto_nprobe()決定,算式是約 0.5 × √(群數),下限 10、上限 2048。README 說目標是約 0.95 的召回率。 - I/O:posting 頁用
PrefetchBuffer預讀(程式註解寫的是 posix_fadvise WILLNEED);回 heap 重排那一步用 PostgreSQL 的read_streamAPI 批次非同步讀取。
跟常見做法比
| pgvector IVFFlat | pgvector HNSW | PRISM | |
|---|---|---|---|
| 結構 | 分群 | 圖 | 階層式分群 |
| 群內存什麼 | 全精度向量 | 全精度向量 | RaBitQ 碼 |
| 排除候選 | 無 | 圖搜尋本身 | 誤差上界 |
| 記憶體需求 | 低 | 高 | 低(專案自述) |
官方發表文的說法是,在 1 億筆向量的資料集上,PRISM 在同樣召回率下比 HNSW 快,而且用的機器記憶體只有四分之一。這是專案自述的數字。
同公司另有 pgvectorscale,用 Rust 寫、走 DiskANN 圖索引路線。pg_vectorsearch 用 C 寫,走 IVF 路線。
限制與成熟度
- alpha:README 寫明只供測試;prerelease 之間不保證能升級,要準備刪掉擴充套件和索引重建。
- 更新會讓召回率漂移:
CHANGELOG.md寫,插入和更新可以用,但群不會自動重新分裂,大量變動後分群會變差,要手動prism_rebalance或REINDEX。也不建議先在空表建索引再灌資料,因為索引大小是依建索引當下的列數決定的。 - 刪除不回收空間:VACUUM 把刪除的項目標成 tombstone,掃描會跳過,但頁內空間還不能重用,要
REINDEX。 - 文件寫的是目標:
architecture.md裡「1 bit 估計能濾掉 80–95% 候選」「十億筆時 p99 低於 100ms」屬於設計目標,沒有對應的測試結果。 - 多租戶還在規劃:
architecture.md把多租戶(tenant 分段路由、複合鍵)列為關鍵設計,src/裡目前沒有任何 tenant 相關程式。 - CI 很完整:工作流程包含 sanitizers、Coverity、CodeQL、coverage 與 pgvector 相容性測試。
- 本文沒有實測:以上根據文件與原始碼整理,沒有編譯或跑基準。