pgvector gives you exactly one thing standalone vector databases spend a lot of marketing budget
convincing you that you need: approximate nearest neighbor search, inside the database you're
already running. You get there through a vector column type, a distance operator in
your ORDER BY, and a choice between two index types. That last part — the choice — is
where most of the actual engineering judgment lives, and it's also the part most write-ups skip in
favor of "just use HNSW."
This post doesn't skip it. Distance operators first, then what HNSW and IVFFlat are actually doing structurally, then the tuning knobs, then real benchmark numbers pulled from published sources — not vibes.
Distance operators: what you're actually asking for
Every pgvector query is a nearest-neighbor search expressed through one of three operators in
your ORDER BY clause. Pick the wrong one and your index won't even get used correctly
— the operator has to match how the index was built.
The index you build has to be created against the same operator you plan to query with —
vector_l2_ops, vector_cosine_ops, or vector_ip_ops. Build an
HNSW index with the wrong ops class and your queries will silently fall back to a sequential
scan, which at any real row count means a query that used to take milliseconds now takes seconds.
What the two indexes are actually doing
Both indexes solve the same problem — avoid comparing your query vector against every row — but they get there through structurally different approaches, and that difference is what drives every trade-off below.
IVFFlat — cluster first, search fewer clusters
Inverted File with Flat compression partitions your vectors into a fixed number
of clusters using k-means, called lists. At query time, it identifies the nearest few
clusters to your query vector and only searches inside those — controlled by probes.
It needs a training step to place those clusters, which means it wants data in the table before
you build the index, and it means the clustering quality degrades as your data drifts away from
what it was trained on.
HNSW — a navigable graph, no training required
Hierarchical Navigable Small World builds a multi-layer proximity graph at index time, where each vector is a node connected to its approximate neighbors. Queries enter at the top, sparse layer and descend, narrowing in on the right neighborhood in roughly logarithmic time. There's no clustering step and no training data requirement — every insert incrementally extends the graph, which is why HNSW tolerates write-heavy workloads far better than IVFFlat.
m controls how
many connections each graph node keeps (higher = better recall, more memory), and
ef_construction controls how thorough the build-time search is (higher = better
graph quality, slower build). For IVFFlat, lists is commonly set to
rows / 1000 up to a million rows and sqrt(rows) beyond that, with
probes starting around sqrt(lists) and tuned up from there.
The numbers
Synthetic benchmarks never perfectly match your workload — dimensionality, dataset size, and hardware all move these figures. But the direction of every trade-off below is consistent across independent published sources, and the magnitude is worth knowing before you guess.
Build time
AWS's published pgvector 0.5.0 benchmark. Independent testing across multiple datasets puts IVFFlat's build-time advantage anywhere from roughly 5× to over 40× faster than HNSW, depending on dataset size and dimensionality — the AWS figures above sit toward the lower end of that range.
Index size on disk
HNSW's graph structure costs memory: independent benchmarking found HNSW indexes running roughly 1.3× to 4.5× larger than IVFFlat indexes on the same data, with the gap widening on higher-dimensional datasets. Broader estimates put HNSW's overall memory footprint at 2–5× that of IVFFlat once you account for query-time working memory too.
Query throughput and recall
| Metric | HNSW | IVFFlat |
|---|---|---|
| Out-of-box recall | 95%+ typical | Depends heavily on lists/probes tuning |
| Search time scaling | ~O(log n) | Grows linearly with probes |
| QPS / p99 latency vs. IVFFlat at 99% recall | ~30× better (pgvector 0.7.0) | baseline |
| Recall stability as data grows | Stable, no retrain needed | Drifts — centroids need periodic REINDEX |
| Insert behavior | Incremental, no rebuild | Degrades until next REINDEX |
The actual decision
Strip away the benchmark noise and the decision comes down to two questions: does your data change after the index is built, and does build time or query time matter more to you?
Reach for HNSW when
- Your table gets continuous writes (RAG ingestion, live chat history, growing corpora)
- Query latency and recall matter more than build time
- Dataset is under roughly 10M vectors
- You want good defaults with less tuning
Reach for IVFFlat when
- Dataset is large, mostly static, and rebuilt in batch
- Memory footprint is the binding constraint
- Sub-second build time on ingest matters more than top-tier recall
- You're willing to REINDEX on a schedule as data drifts
For most RAG pipelines and semantic search workloads — the common case for anyone reading
this — HNSW with sensible defaults (m = 16, ef_construction = 64–200)
is the safer starting point. It costs more to build and more to store, but it tolerates the write
patterns real applications actually have, and it doesn't quietly lose recall while you're not
looking.
IVFFlat earns its place at a specific point on the curve: very large, mostly-static datasets where build time or memory is the binding constraint and you're prepared to own the REINDEX schedule that keeps its clustering honest.
Either way — benchmark on your actual data, your actual dimensionality, and your actual query
patterns before you trust a number from this post or anyone else's. The direction of these
trade-offs is stable. The magnitude, for your workload, is something only your own
EXPLAIN ANALYZE output can tell you.