Je hebt embeddings, je hebt een taalmodel, en nu wil je een vector-database erbij hangen die op je eigen hardware draait. De drie meest gekozen opties zijn Qdrant, Chroma en pgvector — en ze zijn niet uitwisselbaar. Elk heeft een ander kernprincipe, een andere schaalgrenzen, en een andere plek waar het onder druk begint te kraken. Dit artikel legt ze naast elkaar op basis van concrete benchmarks, zodat je een keuze kunt maken zonder achteraf te hoeven migreren.

Wat vector-databases anders maakt dan gewone opslag

Een traditionele database zoekt exact of op index. Een vector-database zoekt op nabijheid: gegeven een queryVector van 1536 dimensies, geef me de 10 opgeslagen vectoren waarvan de cosinusafstand het kleinst is. Dat klinkt eenvoudig, maar brute-force nearest-neighbor zoeken over een miljoen vectoren van 1536 dimensies is te traag voor interactief gebruik — de rekenkosten groeien lineair mee.

De oplossing die de hele sector gebruikt is HNSW (Hierarchical Navigable Small World), een grafstructuur die de zoekruimte opsplitst in lagen. Bij een query begin je bovenin de grafiek en navigeer je steeds nauwkeuriger naar de buurt van je query. Je bereikt sublineaire zoektijden, maar je betaalt een prijs bij het bouwen van de index — en in geheugengebruik, want de volledige HNSW-grafiek moet in RAM passen voor snelle toegang.

Twee parameters sturen het gedrag van HNSW: m (verbindingen per node, typisch 16–64) en ef_construct (kandidaten bij indexbouw, typisch 100–400). Hogere waarden geven betere recall maar vragen meer RAM en bouwtijd. Boven m=64 en ef_construct=512 levert extra verhoging nagenoeg niets meer op. Voor OpenAI-vectoren van 1536 dimensies is m=16, ef_construct=200 een solide standaard.

"HNSW-indexen moeten volledig in RAM passen. Wie dat onderschat, betaalt de prijs bij de eerste nachtelijke ingest van een grote batchupload."

Naast zuiver vector-zoeken heeft RAG in productie bijna altijd behoefte aan hybrid search: combineer dense (semantisch) met sparse (BM25/SPLADE) zoeken, samen te voegen via Reciprocal Rank Fusion. Dense mist exacte productnamen; sparse mist semantische context. Welke database dat out-of-the-box ondersteunt, bepaalt al veel van de keuze.

Qdrant is geschreven in Rust en is van het begin af aan ontworpen als zelfstandige vector-database. Je deployt het als enkele binary of Docker-container, en het levert een HTTP/REST- en gRPC-interface. Voor zelf-hosten is dat prettig: geen Python-runtime nodig, weinig afhankelijkheden, lage overhead.

Op benchmarks met 50 miljoen Cohere-vectoren van 768 dimensies haalt Qdrant bij 99% recall ~41 QPS en een p99-latentie van ~38 ms. Bij 90% recall loopt de doorvoer op naar ~360 QPS. Qdrant is geoptimaliseerd voor lage latentie per query — niet voor parallelle doorvoer op schaal, wat pgvector met pgvectorscale beter doet (zie specs-blok).

Ingest is een sterk punt. Qdrant ondersteunt multi-threaded bulk insert en kan bij het inladen tijdelijk m=0 zetten (HNSW uitschakelen) om de ingest te versnellen, waarna je de index opbouwt. Dat scheelt een factor vier à vijf in verwerkingstijd bij miljoenen vectoren.

De killer-feature voor RAG is de Query API met native hybrid search. Qdrant slaat sparse vectoren (BM25 of BM42, de eigen sparse-embedding aanpak) en dense vectoren op als aparte named vectors op dezelfde collection. Een hybride query ziet er zo uit:

{
  "prefetch": [
    { "query": { "sparse": { "indices": [...], "values": [...] } },
      "using": "sparse",
      "limit": 20 },
    { "query": [0.12, -0.45, ...],
      "using": "dense",
      "limit": 20 }
  ],
  "query": { "fusion": "rrf" }
}

RRF (Reciprocal Rank Fusion) of DBSF (Distribution-Based Score Fusion) samenvoegen de rankings zonder dat je absolute scores hoeft te vergelijken — scores van sparse en dense zoeken zijn immers onvergelijkbaar. Je kunt individuele gewichten meegeven: een gewicht van 3.0 op dense en 1.0 op sparse geeft de voorkeur aan semantische relevantie zonder trefwoord-resultaten volledig te onderdrukken.

Waar Qdrant ingewikkelder is: de configuratie vraagt meer kennisniveau dan Chroma. Schalen naar meerdere nodes vereist een betaald Qdrant Cloud-account of handmatige clusterconfiguratie. En bij 1-op-1 vergelijking met pgvectorscale op parallelle doorvoer verliest Qdrant aan punten (zie specs-blok).

Chroma: Python-first, prototyping in vijf minuten

Chroma is de vector-database voor wie snel iets werkend wil hebben. Je installeert het via pip, je start een client in memory of persistent op schijf, en in tien regels Python heb je embeddings opgeslagen en doorzoekbaar. Er is geen aparte service nodig: in embedded mode draait Chroma in hetzelfde Python-proces als je applicatie.

import chromadb

client = chromadb.PersistentClient(path="/data/chroma")
col = client.get_or_create_collection("docs")
col.add(ids=["1", "2"], documents=["RAG basics", "HNSW explained"])
results = col.query(query_texts=["vector search"], n_results=2)

Die eenvoud heeft een keerzijde. In 2025 is Chroma bezig met een gedeeltelijke herschrijving naar Rust, wat intern 4x snelere schrijf- en leesoperaties oplevert. Maar ten opzichte van Qdrant blijft er verschil: bij filtering haalt Chroma circa 300 QPS met ~20 ms latency; Qdrant zit op ~2.000 QPS met ~6 ms. Chroma heeft sparse vector support toegevoegd (BM25 en SPLADE), maar hybrid search is geen geïntegreerde eerste-klas functie — je combineert zelf twee retrievers en past RRF handmatig toe, of je leunt op LangChain of LlamaIndex. Chroma is praktisch voor corpora tot circa 10 miljoen vectoren; daarboven begin je de Python-overhead te voelen.

pgvector: het is gewoon Postgres

pgvector voegt een vector-datatype toe aan PostgreSQL, samen met HNSW- en IVFFlat-indexen en cosinus/L2/inner-product afstandsfuncties. Je hebt geen nieuwe service nodig. Je hebt geen nieuwe backupstrategie nodig. Je hebt geen nieuwe toegangsbeheer nodig. Als je Postgres al hebt draaien, voeg je één extensie toe:

CREATE EXTENSION vector;

CREATE TABLE documents (
  id       bigserial PRIMARY KEY,
  content  text,
  embedding vector(1536)
);

CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 200);

Hybrid search in Postgres is haalbaar maar volledig handwerk: je combineert ts_rank (full-text via tsvector) met vectorafstand in een CTE en samenvoegt de ranks via RRF. Postgres heeft geen native RRF-operator — de SQL is jouw eigen code en jouw onderhoud.

Waar pgvector verrast: bij hoge parallelle doorvoer met pgvectorscale (de Timescale-extensie die DiskANN-style indexering toevoegt) presteert het bij 99% recall en 50 miljoen vectoren beter dan Qdrant in totale throughput: ~472 QPS versus ~41 QPS. De catch is de p99-latentie: pgvector/pgvectorscale zit op ~74 ms, Qdrant op ~39 ms. Hogere throughput, tragere tail-latencies — klassiek Postgres-gedrag.

De operationele pijnpunten zijn ook klassiek Postgres. HNSW-indexen worden gebouwd bij CREATE INDEX en zijn daarna statisch. Als je parameters wil aanpassen of als de index gedegradeerd is door veel deletes, moet je hem herbouwen — een operatie die uren kan duren en geheugen-intensief is. IVFFlat-indexen verslechteren actief naarmate de vectorverdeling verandert; periodiek herbouwen is geen optie maar noodzaak.

Qdrant vs Chroma vs pgvector — vergelijking
Taal / runtimeQdrant: Rust (binary) · Chroma: Python + Rust core · pgvector: C (Postgres extensie)
Setup complexiteitQdrant: matig (Docker, REST/gRPC config) · Chroma: laag (pip install) · pgvector: laag als Postgres al draait
Query latentie p99 (50M vectoren, 99% recall)Qdrant: ~39 ms · Chroma: n/a (niet op dat schaal) · pgvector+pgvectorscale: ~75 ms
Doorvoer (QPS, 99% recall, 50M vectoren)Qdrant: ~41 QPS · Chroma: ~300 QPS (klein dataset) · pgvector+pgvectorscale: ~472 QPS
Native hybrid searchQdrant: ja (sparse+dense, RRF/DBSF ingebouwd) · Chroma: beperkt (sparse support, geen RRF) · pgvector: handmatig via SQL CTE
Schaallimiet praktischQdrant: miljarden vectoren (multi-node) · Chroma: ~10M vectors aanbevolen · pgvector: 50–100M op single node
Bulk ingest aanpakQdrant: m=0 tijdens ingest, index achteraf · Chroma: standaard · pgvector: CREATE INDEX na ingest
Beste voorQdrant: productie RAG, hoge recall-eisen · Chroma: prototyping, kleine corpora · pgvector: bestaande Postgres-stacks, SQL-queries over metadata
LicentieQdrant: Apache 2.0 · Chroma: Apache 2.0 · pgvector: PostgreSQL License

Welke kies je — en wanneer

RAG-prototype of interne tool voor een klein team? Kies Chroma. In tien minuten operationeel, geen aparte service, Python-API past naadloos bij LangChain en LlamaIndex.

Je hebt al Postgres en je metadata-queries zijn complex? Kies pgvector. Vector-zoeken combineren met gewone SQL-filters (WHERE klant_id = 42 AND taal = 'nl') is triviaal. Geen extra service, backups al geregeld. Accepteer de hogere tail-latentie en plan index-herbouwen in.

Productie-RAG met hoge recall-eisen en hybrid search? Kies Qdrant. De native sparse+dense Query API bespaart zelfgebouwde fusie-code, p99-latentie is consistent laag, en de ingest-strategie (m=0 bij bulkload, index achteraf) maakt grote batchupdates beheersbaar.

Veelgestelde vragen

Welke dimensies worden ondersteund? pgvector heeft een limiet van 16.000 dimensies (ruim voldoende in de praktijk). Qdrant en Chroma hebben geen harde bovenlimiet. Voor OpenAI text-embedding-3-large (3.072 dim) werken alle drie.

Wat is het geheugengebruik van een HNSW-index? Reken ~8 bytes per dimensie per vector plus graafoverhead. Een miljoen vectoren van 1536 dimensies kost ~12 GB vectordata; m=16 voegt ~3–4 GB toe. Op een NAS met 8 GB RAM is dat niet haalbaar.

Werkt pgvector ook met Supabase of Neon? Ja. Beide cloud-Postgres-aanbieders ondersteunen de extensie — dezelfde SQL, geen extra operationele last.

IVFFlat of HNSW in pgvector? IVFFlat bouwt snel maar verslechtert naarmate data verandert; periodieke herbouw is noodzakelijk. HNSW is robuuster en voor RAG de betere keuze.