HNSW vs IVFFlat: Choosing a Vector Index
Exact nearest-neighbour search checks every vector, which gets slow past a few hundred thousand rows. Approximate nearest neighbour (ANN) indexes trade a little recall for a huge speed-up. pgvector offers two: HNSW and IVFFlat.
HNSW (Hierarchical Navigable Small World)
Builds a layered graph where each vector links to its neighbours. Search walks the graph from a coarse top layer down to the detailed bottom layer.
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64); -- the defaults
SET hnsw.ef_search = 80; -- query-time accuracy/speed knob
- Excellent speed/recall trade-off.
- Can be created on an empty table; new rows are added to the graph as they're inserted.
- Slower to build and uses more memory than IVFFlat.
IVFFlat (inverted file)
Clusters vectors into lists groups. A query only searches the closest few groups (probes).
-- Build after the table has data: clusters are learned from existing rows
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops) WITH (lists = 1000);
SET ivfflat.probes = 10;
- Faster to build and smaller than HNSW.
- Needs representative data before you create it; rebuild if the data changes a lot.
- A common starting point is
lists= rows / 1000 (up to 1M rows) andprobes= sqrt(lists).
Which one?
- Default to HNSW for most applications.
- Choose IVFFlat when build time or memory is the main constraint.
- Under ~50k vectors, an exact scan may be fast enough and gives perfect recall.
Filtering gotcha
ANN indexes return the top N before your WHERE filter is applied, so a selective filter can leave you with too few results. Newer pgvector versions support iterative index scans (SET hnsw.iterative_scan = relaxed_order) to keep searching until enough rows pass the filter. Partial indexes per tenant or category are another option.
Comments
No comments yet. Have a question or a tip? Be the first to share it.