IVFFlat vs HNSW in pgvector: Which Index Should You Use?

IVFFlat vs HNSW in pgvector: Which Index Should You Use?

The Problem

pgvector gives you two approximate nearest neighbor (ANN) index types: IVFFlat and HNSW. They solve the same problem -- avoiding brute-force sequential scans over every vector in the table -- but they work in fundamentally different ways, and choosing the wrong one has real consequences for recall, latency, memory, and build time.

IVFFlat partitions vectors into a configurable number of Voronoi cells (lists). At query time, it probes the nearest cells and searches only those vectors. Recall depends heavily on how well the data clusters into those cells and how many probes you set. If your data distribution shifts after the index is built, recall degrades silently -- the cell boundaries are fixed at build time.

HNSW (Hierarchical Navigable Small World) builds a multi-layer graph where each vector is connected to its approximate neighbors. Queries traverse the graph from a random entry point, narrowing in on the nearest neighbors at each layer. It delivers higher recall out of the box with less tuning, but the graph structure consumes significantly more memory than IVFFlat's flat list partitions.

The decision comes down to four factors:

How to Detect It

The first step is identifying which index type you are currently using and whether it is actually being used by the query planner:

-- Compare IVFFlat vs HNSW indexes on the same table
SELECT
    indexname,
    indexdef,
    pg_size_pretty(pg_relation_size(indexname::regclass)) AS index_size
FROM pg_indexes
WHERE indexdef LIKE '%ivfflat%' OR indexdef LIKE '%hnsw%'
ORDER BY indexname;

-- Check index scan frequency
SELECT
    indexrelname,
    idx_scan,
    idx_tup_read,
    idx_tup_fetch
FROM pg_stat_user_indexes
WHERE indexrelname LIKE '%ivfflat%' OR indexrelname LIKE '%hnsw%'
ORDER BY idx_scan DESC;

An index with zero idx_scan values is being ignored by the planner -- either the table is small enough that sequential scans are cheaper, or the query is not using the right operator class. Compare the index_size between the two types: HNSW indexes are typically 2-5x larger than IVFFlat for the same data because the graph structure stores neighbor connections at every layer.

Check whether IVFFlat recall has degraded by comparing results against an exact search:

-- Exact (sequential scan) result for baseline
SET enable_indexscan = off;
SELECT id FROM documents ORDER BY embedding <=> $1 LIMIT 10;

-- Index result for comparison
SET enable_indexscan = on;
SELECT id FROM documents ORDER BY embedding <=> $1 LIMIT 10;

If the index result returns different rows than the sequential scan, your IVFFlat index needs more probes or a rebuild. HNSW rarely shows this kind of drift.

How myDBA.dev Shows It

pgvector page showing IVFFlat and HNSW indexes

myDBA.dev's pgvector monitoring page displays all vector indexes side by side with their type (IVFFlat or HNSW), size, scan counts, and configuration parameters. You can immediately see whether one index type is being used heavily while another sits idle, or whether an IVFFlat index has grown disproportionately large relative to the table size -- a sign that it needs rebuilding with better list parameters.

Queries page showing vector queries

The Queries page complements this by showing the actual vector similarity queries hitting your database. myDBA.dev tracks which index access method each query uses, the average execution time, and how often queries fall back to sequential scans. If you see vector queries consistently bypassing your IVFFlat index in favor of sequential scans, the planner has decided the index is not selective enough -- a strong signal to either increase probes or switch to HNSW.

How to Fix It

If your IVFFlat index is underperforming -- low recall, planner bypasses, or degraded accuracy after data distribution changes -- switching to HNSW is usually the right call:

-- HNSW: better for most workloads (higher recall, no data-dependent quality)
CREATE INDEX CONCURRENTLY idx_docs_hnsw
ON documents USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);

-- IVFFlat: better for very large datasets where build time matters
-- CRITICAL: Load ALL data BEFORE creating the index
CREATE INDEX CONCURRENTLY idx_docs_ivfflat
ON documents USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 1000);

-- Drop the slower index after benchmarking
DROP INDEX CONCURRENTLY idx_docs_ivfflat;

The HNSW parameters m (connections per node) and ef_construction (search width during build) control the quality-speed tradeoff. Higher values improve recall but increase build time and memory. The defaults (m = 16, ef_construction = 64) are conservative -- setting ef_construction = 200 produces a higher-quality graph at the cost of a longer build.

For IVFFlat, the lists parameter determines cluster count. A common heuristic is lists = sqrt(row_count) for tables under 1M rows, and lists = row_count / 1000 for larger tables. But the critical rule is: never build an IVFFlat index on an empty or partially loaded table. The clustering quality depends entirely on the data present at build time.

At query time, tune the search parameters:

Always benchmark both on your actual queries before committing. Index build time is a one-time cost; query latency is ongoing.

How to Prevent It

Use this decision framework when adding vector indexes to new tables:

| Factor | Choose HNSW | Choose IVFFlat | |--------|-------------|----------------| | Dataset size | Under 50M vectors | 50M+ vectors where build time is critical | | Recall needs | 95%+ required | 90% acceptable with tuning | | Write pattern | Continuous inserts | Bulk load then mostly reads | | Memory budget | Can afford 2-5x index size | Memory-constrained | | Maintenance | Minimal tuning preferred | Willing to rebuild after data shifts |

For most workloads under 50 million vectors, HNSW is the safer default. It requires less tuning, handles incremental writes without quality degradation, and delivers higher recall out of the box. Reserve IVFFlat for very large datasets where HNSW build time or memory consumption is genuinely prohibitive.

Monitor index scan rates and query latencies continuously with myDBA.dev to catch regressions before they reach users. If you see IVFFlat recall dropping after a large data import, that is your signal to rebuild.