Back to Home

pgvector vs pgvectorscale vs VectorChord: comparison

Comparative analysis of PostgreSQL extensions for vector search: pgvector, pgvectorscale and VectorChord. Description of HNSW, StreamingDiskANN, RaBitQ algorithms. Docker infrastructure, bulk loading 1M records, benchmark preparation.

Battle of PostgreSQL vector indexes: who is faster?
Advertisement 728x90

pgvector vs pgvectorscale vs VectorChord: A Performance Comparison for Vector Search in PostgreSQL

Vector search transforms text into numerical embeddings and finds semantically similar documents using proximity metrics. Words with similar meanings cluster together in an N-dimensional space. A classic example: King - Man + Woman ≈ Queen illustrates vector analogies.

Key metrics:

  • Cosine similarity: angle between vectors; normalized vectors yield results equivalent to Euclidean distance.
  • Euclidean distance: straight-line distance between points.

PostgreSQL extensions implement ANN (Approximate Nearest Neighbor) search using IVFFlat, HNSW, and specialized algorithms.

Google AdInline article slot

Top Vector Search Extensions

pgvector

An open-source C extension with the vector type. Supports cosine similarity, L2 distance, and dot product. Index types:

  • IVFFlat: vector clustering.
  • HNSW: hierarchical graph, top-down coarse search.

Requires full index loading into RAM.

pgvectorscale

A Rust-based layer on top of pgvector (using PGRX). Features the StreamingDiskANN index (a DiskANN adaptation) that stores data on SSD, reducing RAM usage. Uses SBQ (Statistical Binary Quantization) compression.

Google AdInline article slot

Claimed performance gain: 16x higher throughput on 50M vectors of dimension 768.

pgvecto.rs

Fully written in Rust, supports binary, FP16, and INT8 formats, with dimensions up to 65,535. The VBASE algorithm is optimized for hybrid queries (vector + filters + JOINs).

VectorChord

The active successor to pgvecto.rs by TensorChord. Uses RaBitQ (Randomized Binary Quantization) combined with reordering. Stores 26x more data at the same cost.

Google AdInline article slot

All use the standard vector type, ensuring migration compatibility.

Setting Up the Test Environment

Deploy three databases via Docker Compose:

  • pgvectorscale (timescale/timescaledb-ha:pg18):
services:
  postgres:
    image: timescale/timescaledb-ha:pg18
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/home/postgres/pgdata
      - ./db_init_pgv:/docker-entrypoint-initdb.d

Initialization:

CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS vectorscale;
  • VectorChord (tensorchord/vchord-postgres:pg18-v1.1.1):
CREATE EXTENSION IF NOT EXISTS vchord CASCADE;

Makefile for orchestration:

COMPOSE := docker compose
PGV_FILE := docker-compose-pgv.yml
PGVS_FILE := docker-compose-pgvs.yml
VC_FILE := docker-compose-vc.yml

up-pgv:
	$(COMPOSE) -f $(PGV_FILE) up --build -d
up-pgvs:
	$(COMPOSE) -f $(PGVS_FILE) up --build -d
up-vc:
	$(COMPOSE) -f $(VC_FILE) up --build -d
up: up-pgv up-pgvs up-vc

Unique service names and ports are mandatory.

Extending the Repository for Bulk Operations

The bulk_create method in BaseRepository:

async def bulk_create(self, schemas: list[CreateSchemaType]) -> list[ModelType]:
    instances = [
        self.model(**self._normalize_embedding_format(schema.model_dump()))
        for schema in schemas
    ]
    self.session.add_all(instances)
    await self.session.flush()
    return instances

BulkCreateRequest schema:

class BulkCreateRequest(BaseModel):
    documents: Annotated[
        list[DocumentCreate],
        Field(..., description='List of documents to create', min_length=1, max_length=500)
    ]

Endpoint:

@router.post('/bulk', response_model=list[DocumentResponse], status_code=status.HTTP_201_CREATED)
async def bulk_create_documents(request: BulkCreateRequest):
    async with transaction() as session:
        repo = DocumentRepository(session)
        created_docs = await repo.bulk_create(request.documents)
        return created_docs

Preparing for Benchmarking

Dataset: ag_news from Hugging Face (headlines → embeddings, category → metadata). Goal: 1 million records.

Dependencies:

uv add --dev datasets ollama aiohttp tqdm

Test components:

  • Batch loading from datasets
  • Embedding generation via ollama
  • Bulk insertion into DB
  • Latency measurement for search queries

Test class structure enables parallel comparison across all extensions.

Key Takeaways

  • Compatibility: All extensions use the vector type; code is portable.
  • Scalability: pgvectorscale and VectorChord work with disk storage; pgvector requires RAM.
  • Compression: RaBitQ (26x data density), SBQ for large datasets.
  • Hybrid search: VBASE in pgvecto.rs supports filters + JOINs.
  • Performance: StreamingDiskANN delivers 16x throughput improvement on 50M+ vectors.

— Editorial Team

Advertisement 728x90

Read Next