For faster PostgreSQL vector search, create an approximate pgvector index—HNSW or IVFFlat—using the operator class that matches your query’s distance metric. Approximate indexes trade some recall for speed, so benchmark against exact search with representative data and filters before choosing settings. An index declaration alone does not guarantee that a query uses the index or returns enough qualifying rows.
Choose between HNSW and IVFFlat
pgvector performs exact nearest-neighbor search by default; its documentation describes this as providing perfect recall. Approximate indexes can speed up search, but may return different neighbors. The project documents two approximate index methods:
As an Amazon Associate I earn from qualifying purchases.
| Consideration | HNSW | IVFFlat |
|---|---|---|
| Query speed-recall tradeoff | Better according to the pgvector README; validate on your workload. | Lower than HNSW according to the README; validate on your workload. |
| Build time and memory | Slower to build and uses more memory. | Faster to build and uses less memory. |
| Data needed before index creation | Can be created on an empty table. | Build after loading data because the index has a training step. |
| Main controls | m, ef_construction, and hnsw.ef_search. |
lists and ivfflat.probes. |
These are project-documented tradeoffs, not universal benchmark results. Choose HNSW when its query speed-recall tradeoff suits your workload and its build and memory costs are acceptable. Consider IVFFlat when quicker, lighter index construction matters and you can tune its lists and probes using loaded data.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsMatch the index to the distance operator
The index operator class must match the distance operator used by the query. pgvector’s documented classes are vector_l2_ops for L2 distance, vector_ip_ops for inner product, and vector_cosine_ops for cosine distance. For example, a cosine index and query use:
#1 Best Overall
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);
SELECT *
FROM items
ORDER BY embedding <=> $1
LIMIT 10;
Here, <=> is the cosine distance operator. If you use a different metric, choose its corresponding operator class and query operator; a mismatch prevents the intended index configuration from matching the search.
Tune the index for your workload
HNSW controls
The pgvector README documents defaults of m=16, ef_construction=64, and hnsw.ef_search=40. Treat them as starting points, not guarantees. More construction effort can improve recall but increases build time and insert cost. Increasing search effort can find more candidates, usually at the expense of query speed.
Rank #2
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
SET hnsw.ef_search = 40;
IVFFlat controls
The README suggests starting with roughly rows divided by 1,000 lists for tables up to 1 million rows, and roughly the square root of the row count above 1 million. For probes, it suggests starting around the square root of the list count. These are heuristics, not measured performance promises. More probes improve recall at the cost of speed.
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
SET ivfflat.probes = 10;
The SQL values above illustrate syntax only; select list and probe counts for the table and workload. Build IVFFlat after loading enough rows for the chosen list count: too little training data for that count can reduce the number of results returned.
Rank #3
Account for filters and tenant boundaries
With approximate search, PostgreSQL applies a WHERE filter after scanning index candidates. A selective condition can therefore leave fewer qualifying rows than the requested limit. The README illustrates this with a 10% match rate and default HNSW ef_search of 40: about four matching rows on average. This is an illustration of those values, not a general benchmark.
- For exact filtered search, an index on the filter column may help PostgreSQL find matching rows before ranking them.
- For approximate search, iterative scans can continue searching for candidates when filters leave too few results.
- For a small number of fixed filter values, consider partial indexes.
- For many distinct values, consider partitioning where it fits the data layout.
In a multi-tenant table, a shared approximate index can let one tenant’s vectors affect another tenant’s recall and speed. The project suggests list partitioning or separate tables when tenant isolation makes that concern material.
Iterative scans
Iterative index scans are available starting with pgvector 0.8.0, according to the README. They scan further until enough results are found or a configured maximum is reached. Strict ordering preserves exact distance order; relaxed ordering allows slight deviations in distance order and may improve recall. Check your installed pgvector version before using these settings.
Free tools Windows power users keep installed
One-click scans. No signup required.
SET hnsw.iterative_scan = strict_order;
Use the iterative-scan mode and limits supported by your version, then check both result count and ordering in filtered queries.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Build indexes and verify the query plan
- Load bulk data first when practical. The project recommends adding indexes after initial bulk loading for better loading performance. IVFFlat also needs data for its training step.
- Create the index. Use the chosen method and matching operator class. In production, consider
CREATE INDEX CONCURRENTLYwhen avoiding write blocking is important; account for its operational requirements. - Inspect the query. Run
EXPLAIN (ANALYZE, BUFFERS)on a representative nearest-neighbor query. Confirm the plan uses the intended index and review execution time and buffer activity rather than assuming index creation made the query faster. - Monitor index creation. PostgreSQL’s
pg_stat_progress_create_indexview reports progress; the README documents different build phases for HNSW and IVFFlat. - Compare quality and latency. Test representative vectors, filters, and result limits against exact search. Adjust parameters based on measured speed and recall for your application.
Troubleshoot missing or insufficient results
- The index is not used: check that the query’s ordering operator matches the index operator class, then inspect
EXPLAIN (ANALYZE, BUFFERS). - Filtered search returns fewer rows than the limit: the filter is applied after approximate candidate scanning. Consider iterative scans, an index on the filter column for exact filtered search, partial indexes, or partitioning.
- IVFFlat returns too few neighbors: confirm the table had enough rows for the chosen list count when the index was built; test more probes to trade speed for recall.
- HNSW returns too few neighbors: check
hnsw.ef_search, filters, and dead tuples; iterative scans may help. - Index size or memory pressure is a concern: indexes do not have to fit in memory, though the project notes performance is likely better when they do. Half-precision vectors and binary quantization are documented size-reduction options, but their accuracy and recall effects must be validated for your use case.
For the exact SQL syntax, supported settings, and version-specific details, consult the pgvector project README, Indexing section.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




