VectorChord — Storing and Searching 1 Billion Vectors in a Single PostgreSQL: Why Indexing Is 100x Faster Than pgvector
When building a RAG pipeline, you eventually hit a wall. A separate vector DB, Elasticsearch for keyword search, PostgreSQL for metadata filtering... the moment your infrastructure splits into three systems, operational complexity explodes. You get Slack alerts from three different places, and when something breaks you have to figure out which system failed first — and costs come in separate bills for each. If you've ever thought "can't we just do everything in one PostgreSQL?" — VectorChord might be your answer.
VectorChord is a PostgreSQL extension, an open-source project that enables large-scale vector similarity search. Its predecessor was a project called pgvecto.rs, which the TensorChord team fully redesigned and released under a new name. pgvecto.rs was designed around HNSW and showed performance limitations in high-update environments; VectorChord solved that problem by rewriting everything from scratch using an IVF-based approach. I initially thought "why another one when pgvector exists?" — but the benchmark numbers changed my mind. The team officially declared production readiness with the 1.0 GA release in December 2025, and version 1.1 is currently available.
With VectorChord, you can build indexes 100x faster than pgvector HNSW, while handling vector search, BM25 keyword ranking, and multi-vector Late Interaction — all within a single PostgreSQL stack.
Core Concepts
IVF + RaBitQ: Why This Combination Is Fast
pgvector's HNSW is a graph-based index. It delivers good search quality, but every time a new vector is inserted, the graph must be rebalanced. In environments where data changes frequently, this rebalancing cost becomes a bottleneck.
VectorChord takes a different approach. It assigns vectors to clusters using an IVF (Inverted File Index) structure and manages them via posting lists for each cluster. Insertion is just a single append — no need to touch the global graph. For deletions, they are handled as code clears, but there is one thing worth knowing: deleted vectors remain as dead entries in the posting list until VACUUM runs, which can affect search quality. For workloads with frequent deletions, it's worth planning your VACUUM schedule separately.
RaBitQ (Randomized Bit Quantization) is the key ingredient here. It first randomly rotates the 32-bit floating-point vector, then binarizes it and compresses it into bit-packed integer codes. This is fundamentally different from simply reducing precision like bfloat16. The random rotation step distributes quantization error evenly regardless of direction, minimizing precision loss. At query time, instead of complex floating-point arithmetic, posting lists are scanned using only bit operations and integer arithmetic — meaning a single query is almost entirely resolved within the CPU cache.
IVF (Inverted File Index): The same inverted index structure used in search engines. It divides the vector space into multiple clusters and maintains a list of vectors (posting list) belonging to each cluster. When a query arrives, only the nearest few clusters are searched, making it far faster than a full scan.
Supported Distance Functions
| Distance Function | Operator | Use Case |
|---|---|---|
| Cosine | <=> |
Directional similarity (general text embeddings) |
| L2 | <-> |
Euclidean distance |
| Inner Product | <#> |
Score-based ranking |
| MaxSim | @# |
Multi-vector Late Interaction (ColBERT, etc.) |
MaxSim is what sets VectorChord apart. For models like ColBERT that use token-level multiple vectors, it allows you to aggregate the maximum similarity between query tokens and document tokens — directly inside PostgreSQL.
VectorChord Suite: All-in-One Architecture
The direction the VectorChord team has been pushing recently is interesting. Rather than stopping at vector search alone, the goal is an integrated setup running three capabilities in a single PostgreSQL instance.
| Component | Role |
|---|---|
| VectorChord | Vector similarity search (IVF + RaBitQ) |
| VectorChord-BM25 | Keyword BM25 ranking index |
| pg_tokenizer | Multilingual tokenizer including Korean |
BM25: An advancement of TF-IDF that considers both term frequency and document length for keyword-based ranking. It has been the standard in traditional search engines; VectorChord-BM25 implements it natively in PostgreSQL, providing BM25 ranking 3x faster than Elasticsearch.
EDB (EnterpriseDB) officially adopting VectorChord-BM25 for their AI platform further reinforces confidence in this direction.
Practical Application
That covers the concepts — now let's look at three scenarios for actual usage. The difficulty increases from basic vector search, to hybrid search, to multi-vector.
Example 1: Index Setup in a Large-Scale RAG Pipeline
This is the most basic usage pattern. Using the vchordrq index type applies the IVF + RaBitQ combination.
-- Enable VectorChord extension
CREATE EXTENSION IF NOT EXISTS vchord;
-- Documents table (example with 768-dimensional embeddings)
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
content TEXT,
embedding VECTOR(768)
);
-- Create vchordrq index
-- lists: number of IVF clusters. Adjust based on data size
CREATE INDEX ON documents USING vchordrq (embedding vector_cosine_ops)
WITH (options = $$
residual_quantization = true
[build.internal]
lists = [4096]
spherical_centroids = false
$$);
-- probes: number of clusters to search at query time. Higher = more accurate but slower
SET vchordrq.probes = 10;
-- Semantic search
-- $1 example: '[0.12, -0.07, 0.34, ...]'::vector(768)
SELECT id, content, embedding <=> $1::vector AS distance
FROM documents
ORDER BY distance
LIMIT 10;| Parameter | Description |
|---|---|
residual_quantization |
Enable residual quantization. Re-quantizes the difference from the cluster centroid to improve precision |
lists |
Number of IVF clusters. Recommended ~1000–4000 per 1 million data points |
spherical_centroids |
Set to true when using cosine distance — places cluster centroids on the unit sphere for improved precision |
vchordrq.probes |
Number of clusters to search at query time. Start with roughly 1/10–1/20 of lists and experiment |
Tips for setting the
listsvalue: Too few relative to data size increases scan cost as each cluster holds more vectors; too many increases build time and memory. For 100 million entries, experimenting in the 4096–16384 range is recommended.
Example 2: Hybrid Search (Semantic + BM25 Keyword)
This is a situation you often encounter in practice — pure vector search alone is weak for exact proper nouns or code identifiers. Brand names like "OpenAI" or exact identifiers like useEffect are far more reliably found via keyword matching than semantic similarity. Combining BM25 with RRF (Reciprocal Rank Fusion) lets the two approaches compensate for each other's weaknesses.
-- VectorChord-BM25 extension also required
CREATE EXTENSION IF NOT EXISTS vchord_bm25;
-- Add BM25 index column
ALTER TABLE documents ADD COLUMN content_bm25 bm25vector;
-- k=60 is the standard constant for RRF
WITH semantic AS (
SELECT id, RANK() OVER (ORDER BY embedding <=> $1::vector) AS rank
-- $1 example: '[0.12, -0.07, 0.34, ...]'::vector(768)
FROM documents
ORDER BY embedding <=> $1::vector
LIMIT 60
),
keyword AS (
SELECT id, RANK() OVER (ORDER BY content_bm25 <&> $2::bm25vector) AS rank
-- $2 example: to_bm25vector('PostgreSQL vector search')
FROM documents
ORDER BY content_bm25 <&> $2::bm25vector
LIMIT 60
)
SELECT
COALESCE(s.id, k.id) AS id,
-- Use 1000 instead of rank to penalize results that appear in only one search
1.0 / (60 + COALESCE(s.rank, 1000)) + 1.0 / (60 + COALESCE(k.rank, 1000)) AS rrf_score
FROM semantic s
FULL OUTER JOIN keyword k ON s.id = k.id
ORDER BY rrf_score DESC
LIMIT 10;RRF (Reciprocal Rank Fusion): An algorithm for combining rankings from multiple search results. It converts each system's rank into
1/(k + rank)form and sums them. The advantage is fair rank-based aggregation even when score scales differ.
Example 3: ColBERT Multi-Vector Search (MaxSim)
Honestly, this feature was the decisive reason I took a second look at VectorChord. ColBERT is a reranking model that represents documents not as a single embedding but as multiple vectors per token. Because it selects the maximum similarity between each query token and each document token and sums them, it enables more precise semantic matching than single-embedding approaches. Handling this properly in traditional vector databases has been difficult — VectorChord supports it natively.
ColBERT / ColQwen2: Models that generate token-level multiple embeddings. A single document is represented not as one vector but as an array of vectors equal to the number of tokens. ColQwen2 extends the ColBERT approach to multimodal inputs like images and PDFs as a Vision-Language model.
-- ColBERT style: store document token vectors as an array
-- VECTOR(128)[] is an array type of 128-dimensional vectors
-- VectorChord's vchordrq index directly supports this variable-length array type,
-- and unlike pgvector's single vector type, it can manage per-token multiple vectors in a single column
CREATE TABLE colbert_documents (
id BIGSERIAL PRIMARY KEY,
content TEXT,
token_embeddings VECTOR(128)[] -- array of 128-dimensional vectors per token
);
-- Create MaxSim index
CREATE INDEX ON colbert_documents
USING vchordrq (token_embeddings vector_maxsim_ops)
WITH (options = $$
[build.internal]
lists = [1024]
$$);
-- MaxSim search with query token vector array
-- $1 example: ARRAY['[0.1, 0.2, ...]'::vector(128), '[0.3, 0.1, ...]'::vector(128)]
SELECT id, content
FROM colbert_documents
ORDER BY token_embeddings @# $1::vector[] DESC
LIMIT 10;After WARP (a kernel optimization technique for MaxSim query execution) was applied, a reported evaluation query set that took 810 seconds was reduced to 41 seconds — roughly an 18.7x difference. The same structure can be applied directly to ColQwen2-based pipelines that search PDFs as images without OCR.
Pros and Cons Analysis
Advantages
| Item | Details |
|---|---|
| Indexing speed | 100x faster index build vs. pgvector. Handles 100 million vectors in under 20 minutes |
| Disk efficiency | Minimized memory footprint via RaBitQ quantization. Can serve 4 million vectors on a $1/month instance |
| Update-friendly | IVF posting list structure is more stable than HNSW for frequent inserts and deletes |
| Full SQL compatibility | JOINs, WHERE filters, and transactions all handled with standard SQL |
| Multi-vector support | MaxSim + WARP makes ColBERT-style Late Interaction practical |
| All-in-One stack | BM25 + vector search in a single DB reduces operational complexity |
| Billion-scale | Scales to 1 billion vectors without architectural changes |
Disadvantages and Caveats
| Item | Details | Mitigation |
|---|---|---|
| Index build memory | Index build not possible on instances with 4GB or less | Build on an 8GB+ instance and migrate, or use external KMeans pre-computation |
| External KMeans required | Pre-computing cluster centroids on a separate machine is recommended for optimal performance | Use cloud spot instances short-term to minimize cost |
| x86 optimization bias | fast-scan kernel is x86_64 only. Performance degrades on ARM (aarch64) | Compare with pgvector HNSW performance before choosing in ARM environments |
| f32 only | Currently only float32 vector type supported. int8 etc. not supported | Convert to f32 if your embedding model outputs a different type |
| Build dependencies | Requires clang-17+. Manual installation needed on Ubuntu 22.04, Debian 12 | Use official Docker image (tensorchord/vchord-postgres) |
| Ecosystem maturity | Smaller community and less cloud managed service support compared to pgvector | Self-host or evaluate TensorChord Cloud |
I actually ran it on an ARM instance, and got about half the published benchmark figures. On M1/M2 Mac or AWS Graviton environments, measuring in your own environment first is essential before committing to specs.
When to choose HNSW vs. IVF: If your dataset is in the hundreds of thousands or smaller, or fits entirely in memory, pgvector HNSW may still be the simpler and sufficient choice. VectorChord shines at the hundreds-of-millions to billions scale, in environments with frequent updates.
Most Common Mistakes in Practice
-
Leaving
listsat the default value: A cluster count mismatched to data size affects both search quality and speed. Adjust thelistsvalue proportionally to your data size, and tune the query search range withSET vchordrq.probes = N— the difference is noticeable. -
Building a production index without external KMeans: Internal KMeans builds do work, but at large scale, injecting pre-computed cluster centroids from an external source is better for both quality and speed. Refer to the
centroidinjection option in the official documentation. -
Expecting published benchmarks on ARM environments: All published figures are based on x86_64. On M1/M2 Mac or AWS Graviton, actual measurements can differ significantly — verify in your own environment before finalizing specs.
Closing Thoughts
If you're currently running your RAG stack across three separate services, now is the time to consider consolidating with VectorChord Suite. Instead of managing a vector DB, keyword engine, and multi-vector reranker separately, you can handle everything with familiar SQL — reducing both operational costs and infrastructure complexity at once.
Three steps to get started right now:
-
Spin up an environment instantly with Docker: The command below starts a PostgreSQL container with VectorChord included — no separate compilation needed.
bashdocker run --rm -p 5432:5432 -e POSTGRES_PASSWORD=postgres tensorchord/vchord-postgres:pg17-latest -
Compare index performance on a small dataset: If you're already using pgvector, try creating a
vchordrqindex in parallel on the same dataset of 100K–500K records and compare execution plans and times directly withEXPLAIN ANALYZE. -
Set up a hybrid search pipeline: Install the VectorChord-BM25 extension alongside it, apply the RRF query example above to your current project, and you'll experience accuracy improvements that were hard to achieve with vectors alone.
References
- VectorChord Official Site
- VectorChord GitHub
- VectorChord Official Documentation
- VectorChord 1.0: 100x Faster Indexing than pgvector
- VectorChord: Store 400k Vectors for $1 in PostgreSQL
- VectorChord 0.5: RaBitQ-empowered DiskANN Index
- VectorChord 0.3: Multi-Vector Contextual Late Interaction
- PostgreSQL Vector Search: VectorChord vs. pgvector vs. pgvectorscale
- Hybrid search with Postgres Native BM25 and VectorChord
- VectorChord-BM25: 3x Faster than Elasticsearch
- All-in-one VectorChord Suite: Building Production-Ready RAG
- Scaling Vector Search to 1 Billion on PostgreSQL
- ColBERT rerank in PostgreSQL
- PGXN vchord Distribution Page
- EDB Postgres AI - Official Adoption of VectorChord-BM25