R_REDDYX.XYZ
RAG and Search2026-06-30

Vector Databases 2026: Qdrant, Milvus, pgvector, Chroma Benchmark on Real Workload

Vector Databases 2026: Qdrant, Milvus, pgvector, Chroma Benchmark on Real Workload
TL;DR: For RAG with up to several million vectors and an existing Postgres, choose pgvector — fewer moving parts. For tens of millions of vectors with strict filtering and low latency, choose Qdrant. Milvus — when dealing with hundreds of millions to a billion vectors and the team is ready for distributed infrastructure. Chroma — prototype and local development, not for production under load.

WHY A SEPARATE VECTOR DATABASE IN 2026

Over the past two years, RAG has stopped being hype and become boring infrastructure. Embeddings are generated in batches, documents are updated incrementally, and in production what matters is not demo beauty but p99-latency under real QPS and memory cost per million vectors. It is on these metrics that the four popular engines — vector database of the new generation — diverge the most.

The key difference in 2026: almost everyone has switched to HNSW as the base index, and competition has moved to metadata filtering, quantization, and hybrid (dense + sparse) search. Fresh engine releases are tracked in the REDDYX catalog, and the pace there does not drop.

PARTICIPANTS AND THEIR ARCHITECTURAL POSITION

Qdrant

Written in Rust, single-binary, but can operate in a cluster. Strong point — filtering: payload indexes allow pre-filtering without destroying the HNSW graph. There is scalar and binary quantization, which cuts memory usage by multiples. In practitioners' experience — the most predictable tail latency under mixed load.

Milvus

Distributed system out of the box: compute and storage are separated, with separate nodes for query, data, and index. Scales to a billion vectors, but you pay for operational complexity — etcd, object storage, message queue. For a single service, this is overkill. There is a lightweight Milvus Lite mode for local use.

pgvector

Not a separate database, but a Postgres extension. That’s why it wins in projects where Postgres is already present: transactions, JOINs with business data, backups, permissions — all free. With the introduction of the HNSW index and iterative scanning for filters, pgvector has covered most RAG scenarios up to several million rows.

Chroma

Optimized for developer experience: three lines of Python and you have a working search. Great for prototyping, laptop, local RAG. Under serious competitive QPS and large volumes it historically sags — it is a development-stage tool, not for high load.

METHODOLOGY: HOW TO MEASURE HONESTLY

Any benchmark of vector databases lies if you don’t fix three things: recall, index config, and workload character. Comparing latency at recall@10 = 0.95 versus recall@10 = 0.80 is comparing different tasks. The de facto standard in the community is the ann-benchmarks methodology: we plot a recall/QPS curve, not a single number.

  • Recall — we fix the target (usually 0.95 or 0.99) and tune parameters to it.
  • HNSW parametersM, ef_construction on write and ef_search on read determine everything.
  • Workload — pure ANN, ANN + filter, concurrent writes during reads give radically different results.
  • Dimensionality — 768 (bge, e5) and 1536 (OpenAI) behave differently in memory and speed.

COMPARISON SUMMARY TABLE

The numbers below are community‑measurement averages and typical prod configs on a dataset of roughly 1–5 million vectors, 768‑dimensional, recall@10 ≈ 0.95, one medium‑size node. These are ballpark figures, not absolute truth — on your hardware and data everything will shift.

CriterionQdrantMilvuspgvectorChroma
Core languageRustGo / C++C (in Postgres)Rust / Python
IndexHNSW + quantizationHNSW, IVF, DiskANNHNSW, IVFFlatHNSW
p99 latency (single node)lowmediummedium–lowmedium–high
Peak RPShighvery high (cluster)mediumlow
Metadata filteringexcellent (payload indexes)goodgood (regular WHERE)basic
Scale ceilingtens of millions+billiona few million comfortablyhundreds of thousands
Operational complexitylowhighminimalminimal
Hybrid searchyes (sparse + dense)yespartially (via FTS)limited

WHAT HAPPENS UNDER LOAD: WEAK POINTS

Filtering breaks ANN

The main pitfall in production. Naive approach — first find nearest neighbors, then filter by tenant_id or category. If the filter cuts off 99% of the data, you’ll get almost empty result or recall will plummet. Qdrant solves this with payload indexes and filter cardinality estimation; pgvector in recent versions — iterative index scanning. This is where Chroma and naive integrations most often break.

Memory and Quantization

A million 1536-dimensional float32 vectors is about 6 GB just for raw data, plus the HNSW graph. Scalar quantization (int8) reduces this roughly 4× with minimal recall loss; binary quantization is far more aggressive but requires rescoring. If RAM budget is limited, quantization in Qdrant/Milvus is not an option — it’s a mandatory step.

Write During Read

HNSW is expensive on inserts. With a steady stream of updates (fresh documents every minute), concurrent write hurts read latency. Milvus separates this across different nodes, Qdrant uses segments, pgvector inherits Postgres MVCC with all the pros and cons of vacuum.

CODE: THE SAME TASK ON TWO ENGINES

Below — search with metadata filter, as it really looks in a RAG pipeline. First, Qdrant.

from qdrant_client import QdrantClient
from qdrant_client.models import Filter, FieldCondition, MatchValue

client = QdrantClient(url="http://localhost:6333")

# поиск ближайших с ЖЁСТКИМ фильтром по tenant и языку
hits = client.query_points(
    collection_name="docs",
    query=query_vector,          # list[float], 768 dim
    query_filter=Filter(
        must=[
            FieldCondition(key="tenant_id", match=MatchValue(value=42)),
            FieldCondition(key="lang", match=MatchValue(value="ru")),
        ]
    ),
    limit=10,
    search_params={"hnsw_ef": 128},   # управляем recall/latency
    with_payload=True,
).points

for h in hits:
    print(h.score, h.payload["title"])

The same logic on pgvector — plain SQL, the filter is just a WHERE, and proximity is computed by the <=> operator (cosine):

-- индекс создаётся один раз
CREATE INDEX ON docs USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

-- для нормального recall при фильтрации
SET hnsw.ef_search = 128;
SET hnsw.iterative_scan = 'relaxed_order';

SELECT title, embedding <=> :query_vector AS distance
FROM docs
WHERE tenant_id = 42 AND lang = 'ru'
ORDER BY embedding <=> :query_vector
LIMIT 10;

The difference in philosophy is obvious right away: Qdrant is a specialized API with explicit graph control, pgvector is familiar SQL where the vector is just another column type alongside business data.

HOW TO CHOOSE FOR YOUR SCENARIO

  1. Already have Postgres, volume up to a few million vectors — pgvector. Don’t proliferate infrastructure for a task that the extension solves. Joins with metadata and transactions are worth it.
  2. Tens of millions of vectors, strict multi-tenant filtering, need low p99 — Qdrant. Optimal balance of speed, filtering, and operational simplicity.
  3. Hundreds of millions to a billion, separate platform team — Milvus. Only if you’ve truly hit the ceiling and are ready for distributed deployment.
  4. Prototype, local development, demo — Chroma. Quick start, then migrate if the project takes off.

A common mistake — pulling Milvus into an MVP ‘for growth’. In 2026, migration between engines takes a day or two (embeddings are portable), while the operational pain of premature distribution is constant. Start with the simplest option that fits your volume.

Frequently Asked Questions

Which is faster — Qdrant or pgvector?

With equal recall on medium volumes (up to a few million vectors), the difference is small, and pgvector is often fast enough. On tens of millions and under concurrent filtering, Qdrant more steadily holds p99-latency thanks to payload indexes and filter cardinality estimation.

Is pgvector enough for production?

Yes, for most RAG projects up to a few million vectors. You need an HNSW index, setting ef_search, and enabling iterative scanning during filters. You’ll start to hit limits on tens of millions of rows or extreme QPS.

Is quantization needed?

If the volume exceeds several million 1536-dimensional vectors and RAM is limited — yes. Scalar quantization (int8) reduces memory roughly fourfold with nearly unchanged recall. Binary quantization is more aggressive but requires rescoring on the original vectors.

Why not just Chroma for everything?

Chroma is brilliant during development, but under high competitive QPS, large datasets, and complex filtering it lags in latency and throughput. It's a prototype tool, not a high-load production.

Vector engines in 2026 are updated literally weekly — new quantization modes, GPU indexes, hybrid search. To not miss a release that will save you half of your RAM, check out the Telegram-канал REDDYX AI — new repositories every 30-60 minutes.

New repositories every 30 minutes

REDDYX AI scans GitHub 24/7 and ships the best AI/ML/Web3 projects to Telegram.

Join on Telegram

← All articles

Vector Databases 2026: Qdrant, Milvus, pgvector, Chroma Benchmark on Real Workload | REDDYX AI