Powrót do strony głównej

pgvector vs pgvectorscale vs VectorChord: porównanie

Porównawcza analiza rozszerzeń PostgreSQL dla wyszukiwania wektorowego: pgvector, pgvectorscale i VectorChord. Opis algorytmów HNSW, StreamingDiskANN, RaBitQ. Infrastruktura Docker, bulk-wgrywanie 1M rekordów, przygotowanie benchmarków.

Bitwa indeksów wektorowych PostgreSQL: kto jest szybszy?
Advertisement 728x90

Porównanie rozszerzeń pgvector, pgvectorscale i VectorChord do wyszukiwania wektorowego w PostgreSQL

Wyszukiwanie wektorowe przekształca tekst w liczbowe embeddingi i znajduje dokumenty podobne pod względem znaczenia za pomocą metryk bliskości. Embeddingi słów o podobnym znaczeniu grupują się w przestrzeni N-wymiarowej. Klasyczny przykład: King - Man + Woman ≈ Queen pokazuje analogię wektorową.

Główne metryki:

  • Podobieństwo kosinusowe: kąt między wektorami, znormalizowane wektory dają równoważność z odległością euklidesową.
  • Odległość euklidesowa: prosta odległość między punktami.

Rozszerzenia PostgreSQL realizują wyszukiwanie ANN (przybliżony najbliższy sąsiad) z indeksami IVFFlat, HNSW i specjalistycznymi algorytmami.

Google AdInline article slot

Rozszerzenia do wyszukiwania wektorowego

pgvector

Otwarte rozszerzenie napisane w języku C z typem vector. Obsługuje podobieństwo kosinusowe, L2 oraz iloczyn skalarny. Indeksy:

  • IVFFlat: klasteryzacja wektorów.
  • HNSW: hierarchiczny graf, surowe wyszukiwanie od góry do dołu.

Wymaga pełnego załadowania indeksu do pamięci RAM.

pgvectorscale

Nadbudowa nad pgvector napisana w Rust (PGRX). Indeks StreamingDiskANN (adaptacja DiskANN) przechowuje dane na SSD, z niskim obciążeniem pamięci RAM. Kompresja SBQ (Statistical Binary Quantization).

Google AdInline article slot

Zgłoszony wzrost wydajności: 16-krotny przyrost przepustowości dla 50M wektorów o wymiarze 768.

pgvecto.rs

Całkowicie napisane w Rust, obsługa binary/FP16/INT8, wymiar do 65535. Algorytm VBASE zoptymalizowany pod zapytania hybrydowe (wektor + filtry + JOIN).

VectorChord

Aktywna alternatywa dla pgvecto.rs od TensorChord. Kompresja RaBitQ (Randomized Binary Quantization) + ponowne uporządkowanie. Przechowuje 26 razy więcej danych przy tej samej kosztowności.

Google AdInline article slot

Wszystkie korzystają ze standardowego typu vector, migracje są kompatybilne.

Konfiguracja infrastruktury testowej

Wdrożenie przez Docker Compose dla trzech baz danych:

  • 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

Inicjalizacja:

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 do zarządzania:

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

Unikalne nazwy usług i porty są wymagane.

Rozszerzenie repozytorium dla operacji bulk

Metoda bulk_create w 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

Schemat BulkCreateRequest:

class BulkCreateRequest(BaseModel):
    documents: Annotated[
        list[DocumentCreate],
        Field(..., description='Lista dokumentów do utworzenia', 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

Przygotowanie do benchmarków

Zbiór danych: ag_news z Hugging Face (tytuły → embeddingi, category → metadane). Cel: 1 milion rekordów.

Zależności:

uv add --dev datasets ollama aiohttp tqdm

Składniki testu:

  • Ładowanie partiami z datasets
  • Generowanie embeddingów przez ollama
  • Wczytywanie masowe do bazy danych
  • Pomiar opóźnienia wyszukiwania

Struktura klasy testowej została zaimplementowana do równoległego porównania wszystkich rozszerzeń.

Co jest ważne

  • Kompatybilność: wszystkie rozszerzenia używają typu vector, kod jest przenośny.
  • Skalowanie: pgvectorscale i VectorChord działają z dyskiem, pgvector wymaga RAM.
  • Kompresja: RaBitQ (26-krotna gęstość), SBQ dla dużych zbiorów danych.
  • Wyszukiwanie hybrydowe: VBASE w pgvecto.rs do filtrów + JOIN.
  • Wydajność: StreamingDiskANN daje 16-krotny wzrost przy 50M+ wektorach.

— Editorial Team

Advertisement 728x90

Czytaj dalej