A PostgreSQL expansion for high-performance approximate nearest neighbor (ANN) search.
Status: alpha. Meant for testing, not production. Upgrading between prereleases may not be possible; scheme to autumn and recreate the extension and its indexes. See the changelog for known issues.
- PRISM index (100M+ vectors) — IVF-style, alongside hierarchical routing
- RaBitQ quantization alongside theoretical error limits for two-stage search
- SIMD-optimized extend computation (AVX2, AVX512, NEON)
- Dynamic updates — inserts, updates and deletes, alongside posting-list splits via prism_rebalance (see Maintenance)
- pgvector compatible — indicator existing vector and halfvec columns as-is (see Existing pgvector tables)
PRISM (Partitioned Routing Index for Similarity Matching) is a PostgreSQL-native index admission method that is conceptually akin to pgvector's IVFFlat, but it is built about state-of-the-art quantization, routing, and partitioning techniques. It is implemented in C for elevated achievement and seamless integration alongside PostgreSQL.
The result: an index that is considerably faster than pgvector's IVFFlat and HNSW, for the two querying and indicator building. In fact, compared to graph-based HNSW, PRISM is faster at equal recall on a 100-million-vector dataset, on an case alongside a fourth of the memory.
To accomplish this performance, PRISM uses a hierarchical centroid tree to partition vectors into clusters, and relies on the PostgreSQL buffer cache to keep that tree heated in memory. Vectors are RaBitQ-quantized and stored in posting lists alongside a leaf layout optimized for SIMD extend computation. The RaBitQ encoding carries error limits that let a hunt regulation out vectors that cannot perchance be nearest neighbors, so far small of them need reranking against the full-precision vectors stored in the table.
Query Flow: 1. Traverse centroid tree to discover nearest clusters (buffer cache) 2. Scan posting lists for candidates (buffer cache, async I/O) 3. RaBitQ two-stage filtering: evaluation → error border → rerank 4. Full-precision reranking from heap 5. Return k nearest neighbors
For detailed architecture, see docs/architecture.md.
Install the expansion 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 another expansion is needed.
- Existing pgvector tables. Already have vector or halfvec columns? Install pg_vectorsearch next to pgvector and build a PRISM indicator on those columns as they are. Your table and queries remain unchanged.
Both paths build the identical indicator and use the identical operators (<->, <#>, <=>) and tuning knobs.
CREATE EXTENSION pg_vectorsearch; -- PLAIN keeps the vector inline. EXTERNAL (the default) toasts a value -- complete ~2 kB, and rerank afterward pays an additional fetch the planner does not cost. CREATE TABLE items ( id serial PRIMARY KEY, embedding vec32(3) STORAGE PLAIN ); -- Load first; the indicator is sized from the row figure at CREATE INDEX. -- 500 rows is adequate 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 merchandise or cosine, see Operator classes below. CREATE INDEX ON items USING prism (embedding vec32_l2_ops); -- 0 = car (~0.95 recall). Raise for recall, lesser for speed. SET prism.nprobe = 40; SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
Use vec16 alternatively of vec32 to halve the area vectors obtain in the table, alongside the matching vec16_* operator classes.
If you already have a array alongside pgvector's vector or halfvec columns, you can build a PRISM indicator on it directly. Nothing concerning the table or your queries needs to change. vec32 and vec16 are binary-compatible alongside pgvector's vector and halfvec but have distinct names, so the two extensions can be installed flank by side, equal in the identical schema.
-- The array you already have, included so the example runs on its own. CREATE EXTENSION vector; CREATE TABLE documents ( id bigserial PRIMARY KEY, build 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 indicator on the -- existing column. The pillar 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 operating as before. PRISM is normally faster, so -- the planner should favor it complete the HNSW indicator during the two exist. -- Verify alongside 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 blessed alongside it, autumn the HNSW index. DROP INDEX documents_embedding_hnsw;
Use the PRISM operator class for the identical distance metric as your HNSW index.
Pick the controller category for your extend metric and pillar 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.
Defaults are tuned from large-scale benchmarks; most deployments only ever modify prism.nprobe. See the tuning guide for all indicator parameter and GUC, their tradeoffs, and whenever changing them makes sense.
An insert appends to whichever posting catalog its vector routes to, so lists grow as rows attain and never divided on their own. Besides REINDEX, the prism_rebalance procedure splits oversized lists in place. See the maintenance guide for how it plant and its current limitations.
Requirements:
- PostgreSQL 18+ (with betterment 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 instal to PostgreSQL meson instal -C builddir
See the development guide for build options.
- Tuning - Index parameters, GUCs, tradeoffs, defaults
- Maintenance - Rebalancing and splitting posting lists
- Architecture - High-level scheme and data structures
- SIMD - SIMD build options and extend computation
- Development - Build options, testing, CI scripts
- RaBitQ - Binary quantization alongside theoretical error bounds (SIGMOD 2024)
- SPFresh - LIRE protocol for incremental updates (SOSP 2023)
- SPANN - Billion-scale ANN alongside border replication (NeurIPS 2021)
- ScaNN for AlloyDB - Hierarchical clustering (Google)
- pgvector - Vector category and operators for PostgreSQL
- pgvectorscale - DiskANN-based vector indicator for PostgreSQL
