pg_vectorsearch (GitHub Repo)
Timescale's new pg_vectorsearch extension introduces the PRISM index, outperforming HNSW and IVFFlat for high-performance vector similarity search in PostgreSQL.
Deep dive
- PRISM implements hierarchical centroid tree partitioning for optimized vector clustering.
- Features RaBitQ quantization, providing theoretical error bounds for two-stage search.
- Leverages SIMD-optimized distance computation (AVX2, AVX512, NEON).
- Provides binary compatibility with pgvector's vector and halfvec column types.
- Supports dynamic updates (inserts, deletes) via internal rebalancing.
- Uses the PostgreSQL buffer cache to keep the centroid tree in memory for faster routing.
Decoder
- ANN (Approximate Nearest Neighbor): A search algorithm that finds near-optimal matches in high-dimensional space significantly faster than exact brute-force search.
- SIMD (Single Instruction, Multiple Data): CPU instructions that perform the same operation on multiple data points simultaneously, significantly accelerating vector math.
- RaBitQ: A quantization technique that compresses vectors while maintaining rigorous error bounds, allowing for efficient two-stage filtering.
- IVFFlat: An indexing method that partitions the vector space into clusters to reduce search scope, commonly used in pgvector.
Original article
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.
- 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 - pgvector compatible — index existing
vectorandhalfveccolumns as-is
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
Usage
Install the extension first.
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 ownvec32(float32) orvec16(float16) columns. No other extension is needed. - Existing pgvector tables. Already have
vectororhalfveccolumns? Installpg_vectorsearchnext to pgvector and build a PRISM index on those columns as they are. Your table and queries stay unchanged.
Native types
CREATE EXTENSION pg_vectorsearch;
CREATE TABLE items (
id serial PRIMARY KEY,
embedding vec32(3) STORAGE PLAIN
);
INSERT INTO items (embedding)
SELECT ARRAY[i::real, (i % 7)::real, (i % 5)::real]::vec32
FROM generate_series(1, 1000) i;
CREATE INDEX ON items USING prism (embedding vec32_l2_ops);
SET prism.nprobe = 40;
SELECT * FROM items
ORDER BY embedding <-> '[3,1,2]'
LIMIT 5;
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.
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);
CREATE EXTENSION pg_vectorsearch;
CREATE INDEX documents_embedding_prism
ON documents USING prism (embedding vec32_l2_ops);
Operator classes
| 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 |
Tuning
Defaults are tuned from large-scale benchmarks; most deployments only ever adjust prism.nprobe.
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.
Building
Requirements:
- PostgreSQL 18+ (with development headers)
- GNU C11 compiler (GCC 11+ or Clang 14+)
- Meson build system
- Optional: CBLAS, for faster RaBitQ encoding.
# Release build
meson setup builddir --buildtype=release
# Build and install to PostgreSQL
meson install -C builddir
License
The PostgreSQL License
Fresh dev news, AI-digested daily. Never miss a trend at devoured.fyi