skip to content

Graph-Based Indexes (HNSW)

HNSW is the default ANN index in most vector stores: a layered proximity graph you descend greedily, trading a slice of recall for search that stays fast as the corpus grows.

on this pageshow

questions

5

Which HNSW parameters are fixed at build time and which can you tune per query?

level: middleimportance: must knowfreq 64%

answer

  1. two knobs at insert, one at query
  2. efSearch rides the request, not the index
  3. M is written into the graph
  4. widening the beam cannot invent missing edges
  5. sweep against brute-force ground truth

basics

~20 s

M and efConstruction are baked into the graph when vectors are inserted, so changing them means rebuilding. efSearch is a query-time knob: raise it per request to buy recall with latency, lower it for cheap traffic.

solid answer

~50 s

HNSW has two build-time knobs and one query-time knob, and the asymmetry drives how you operate it. **M** is the number of neighbour links kept per node per layer (layer 0 typically allows about 2M). It fixes the graph's connectivity and its memory footprint, and it is written into the structure as vectors are inserted. **efConstruction** is the candidate-list width used while choosing those neighbours during insertion — a bigger value explores more of the graph and yields better edges, costing build time but not query time or storage. Both are immutable once built: changing either means a full reindex. **efSearch** is the beam width at query time and can be set per request. A sweep on an 80M-vector catalogue might take efSearch from 32 to 512 and lift recall@10 from 0.86 to 0.99 while tripling p99 latency. So you get recall back cheaply on the query path, but a badly chosen M or efConstruction costs you a rebuild.

go deeper

for a junior

Know the three names and which clock each runs on: M and efConstruction when the index is built, efSearch when a query runs. Say plainly that efSearch is the one you can change without rebuilding.

for a middle

Explain what each parameter does mechanically — M is links per node, efConstruction is the beam used to pick those links, efSearch is the beam used to serve a query — and that changing the first two means a full reindex.

for a senior

Show how you would actually choose efSearch: sample queries, brute-force ground truth, sweep the value, and pick the knee of the recall-latency curve for the product's tolerance. Mention serving different traffic classes at different beam widths.

for a principal

Own the asymmetry as a design risk: a low efConstruction chosen to fit a build window is a decision you cannot walk back without hours of rebuild. Be ready to argue the recall target from product impact rather than from a default.

## Three knobs, two clocks Almost every HNSW deployment is tuned with three parameters, and the single most useful thing to know about them is which clock they run on. **M — build time.** M is the maximum number of bidirectional links each node keeps per layer. Layer 0, which holds every vector, usually allows roughly twice that (implementations expose this as a separate maximum). M controls how densely connected the graph is. More links mean more alternative paths for the greedy walk, so a given beam width finds better neighbours; they also mean more edges to store and more neighbours to score at every hop. **efConstruction — build time.** When a new vector is inserted, HNSW runs the same beam search used at query time to find its candidate neighbours, with the beam width set to efConstruction. Those candidates are then pruned down to M links by a heuristic that prefers a diverse spread of directions rather than simply the M closest — diversity is what preserves long-range navigability. A larger efConstruction means the insertion looked at more of the graph before choosing, so the edges are better. It costs build time and nothing else: it does not change the index size, and it does not appear at query time. **efSearch — query time.** This is the beam width used at layer 0 when serving a query. It must be at least k. It is the only one of the three that a caller can change per request, because it is a parameter of the traversal, not of the structure being traversed. ## Why the asymmetry matters operationally The practical consequence is that recall problems have two very different price tags. If your recall is short and efSearch is low, you are one config change away from fixing it. You can even do it selectively: serve high-value traffic (a checkout recommendation, a compliance search) at efSearch 256 and background or bulk traffic at 64, on the same index, in the same process. Nothing is rebuilt and nothing is redeployed. If your recall is short because M is too small for the data's intrinsic difficulty, or because efConstruction was set low to make an overnight build fit its window, there is no query-time escape. Raising efSearch on a poorly connected graph has diminishing returns — you can widen the beam all you like, but if the edges needed to reach the true neighbour were never written, the traversal cannot follow them. Fixing it is a full rebuild of the whole index, which on a large corpus is hours of compute and a deployment event. That is why the standard advice is to be generous with efConstruction on the first build. It is the cheapest of the three mistakes to avoid and the most expensive to correct. ## Reading the recall-latency curve efSearch tuning is empirical, not analytical. The method is a sweep: hold the index fixed, build a ground-truth set of true neighbours for a few thousand sample queries with an exhaustive scan, then measure recall and latency at efSearch values across a range — 32, 64, 128, 256, 512. The shape is always the same. Recall rises steeply at first and then flattens; latency rises close to linearly with the number of nodes visited, which tracks efSearch. On a large product catalogue you might see recall@10 go 0.86 at efSearch 32, 0.95 at 128, and 0.99 at 512, while p99 latency triples across that range. The interesting part of the curve is the knee — the point past which each further percentage of recall costs disproportionate latency. Where you sit on that curve is a product decision, not a database decision. A retrieval stage feeding a reranker can often run at lower recall than a system whose top-10 is the final answer, because the reranker only ever sees what retrieval returned but the user only ever sees the reranked head. Conversely, a legal or compliance search where a missed document is a real failure sits far up the curve and pays the latency. ## Interactions worth knowing M and efSearch are not independent. A higher-M graph reaches a target recall at a lower efSearch, because each hop offers better options; that is one way to spend memory to buy latency. efConstruction below M is pointless — the beam must be at least as wide as the number of neighbours you are going to select from it. And efSearch below k is invalid, since the beam cannot hold the result set you asked for. ## What interviewers listen for The answer they want is the clean split — M and efConstruction are structural and immutable, efSearch is per-query — plus the operational consequence: one class of recall problem is a config change and the other is a rebuild. Candidates who can also describe how they would actually pick efSearch, by sweeping against a brute-force ground truth rather than guessing, sound like people who have run one of these in production.

  • If efSearch is already 512 and recall is still 0.9, what do you suspect?
    That the graph itself is the limit, not the beam. Widening the search cannot follow edges that were never written, so suspect a low M for the data's difficulty, an efConstruction set too small to pick good neighbours, or a large fraction of tombstoned nodes eating the candidate budget. The diagnosis is to rebuild a sample of the corpus with higher M and efConstruction and re-measure recall at a modest efSearch.
  • Can different queries against the same HNSW index use different efSearch values?
    Yes — efSearch is a parameter of the traversal, so it can vary per request against one index. That makes tiered service practical: latency-sensitive or bulk traffic runs at a low beam width while high-value or recall-critical queries run wide. The only hard constraint is that efSearch must be at least k, since the beam has to be able to hold the requested result set.
  • How does raising M change the efSearch you need?
    A higher-M graph is better connected, so each hop offers more and better options and a given recall target is reached at a lower efSearch. That is a direct memory-for-latency trade: you pay more RAM for edges up front and get cheaper queries. It is worth measuring rather than assuming, because past a point the extra neighbours cost distance computations per hop without improving the paths.

saying these in an interview costs you the question

  • Thinks efSearch can be changed only by rebuilding
  • Believes efConstruction affects query latency
  • Assumes raising efSearch always recovers lost recall
  • Says M has no effect on index memory
  • Picks efSearch by intuition instead of a measured sweep

context

open as a page

How does an HNSW index search its layers to find nearest neighbours?

level: middleimportance: must knowfreq 72%

basics

~20 s

HNSW stacks proximity graphs. Search starts at one entry point in the sparse top layer and greedily hops to closer neighbours, dropping a layer at each local minimum, then runs a widened beam search over the dense bottom layer.

open as a page

How do deletes degrade an HNSW index over time, and what fixes it?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Most implementations tombstone deletes: the vector stays in the graph as a connector and is only filtered out of results. Queries then spend their candidate budget on dead nodes, so latency rises and effective recall falls until compaction or a rebuild.

open as a page

Why does an HNSW index need more RAM than the vectors alone, and how much more?

level: seniorimportance: should knowfreq 52%

basics

~20 s

HNSW stores a neighbour-id list per vector per layer — about 2M ids at layer 0. At M=32 that adds roughly 250 bytes per vector, so an 80M-vector catalogue pays around 20 GB for graph edges alone, on top of the vectors.

open as a page

Your HNSW build takes 11 hours on one machine — how do you plan rebuilds without downtime?

level: principalimportance: should knowfreq 36%

basics

~20 s

Treat the build as a batch job, not an online operation. Shard so builds run in parallel, keep serving the old index while the new one builds on separate capacity, then swap atomically behind the query path. Reserve headroom for two copies.

open as a page