Approximate indexes
By default, pgvector performs exact nearest neighbor searches with perfect recall. To increase search speed, add an approximate index — this trades a small amount of recall for significantly higher throughput. PolarDB for PostgreSQL supports two index types: Hierarchical Navigable Small Worlds (HNSW) and Inverted File with Flat Compression (IVFFlat).
Choose an index type
Start with HNSW. It delivers better query performance at any given recall level and doesn't require training data to build. Use IVFFlat only if your workload has specific constraints, such as tighter memory budgets or very infrequent queries on static datasets.
| HNSW | IVFFlat | |
|---|---|---|
| Algorithm | Multilayer graph | Partitioned lists |
| Build time | Slower | Faster |
| Memory usage | Higher | Lower |
| Query performance | Better speed-recall tradeoff | Lower speed-recall tradeoff |
| Requires training data | No — build on empty tables | Yes — build after loading data |
HNSW
Create an HNSW index
Create a separate index for each distance function your queries use:
| Distance | Operator class |
|---|---|
| Euclidean (L2) | vector_l2_ops |
| Inner product | vector_ip_ops |
| Cosine | vector_cosine_ops |
| Manhattan (L1) | vector_l1_ops |
| Hamming | bit_hamming_ops |
| Jaccard | bit_jaccard_ops |
Euclidean distance (L2)
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);Use the operator class that matches your vector type. For halfvec, use halfvec_l2_ops instead of vector_l2_ops. For sparsevec, use sparsevec_l2_ops.
Inner product
CREATE INDEX ON items USING hnsw (embedding vector_ip_ops);Cosine distance
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);Manhattan distance (L1)
CREATE INDEX ON items USING hnsw (embedding vector_l1_ops);Hamming distance
CREATE INDEX ON items USING hnsw (embedding bit_hamming_ops);Jaccard distance
CREATE INDEX ON items USING hnsw (embedding bit_jaccard_ops);Supported vector types:
| Type | Description | Limit |
|---|---|---|
vector | Standard float vectors | Up to 2,000 dimensions |
halfvec | Half-precision vectors | Up to 4,000 dimensions |
sparsevec | Sparse vectors | Up to 1,000 non-zero elements |
bit | Binary vectors | Up to 64,000 dimensions |
Index creation options
| Parameter | Default | Description |
|---|---|---|
m | 16 | Maximum connections per layer. Higher values improve recall but increase memory usage and build time. |
ef_construction | 64 | Size of the dynamic candidate list during graph construction. Higher values improve recall but slow down indexing. |
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops) WITH (m = 16, ef_construction = 64);Query options
| Parameter | Default | Description |
|---|---|---|
hnsw.ef_search | 40 | Size of the dynamic candidate list during search. Higher values improve recall but slow down queries. |
Set hnsw.ef_search for the current session:
SET hnsw.ef_search = 100;To apply it to a single query only, use SET LOCAL inside a transaction:
BEGIN;
SET LOCAL hnsw.ef_search = 100;
SELECT ...
COMMIT;IVFFlat
IVFFlat divides vectors into lists and searches the subset of lists closest to the query vector. Build the index after loading your data — the quality of the list partitioning depends on the data distribution.
Get good recall
Follow these three steps:
Create the index after the table has data. IVFFlat requires training data to partition vectors effectively.
Choose the right number of lists:
Up to 1 million rows:
lists = rows / 1000Over 1 million rows:
lists = sqrt(rows)
Set the number of probes at query time: Use
probes = sqrt(lists)as a starting point. More probes improve recall but slow down queries.
Create an IVFFlat index
Create a separate index for each distance function your queries use:
| Distance | Operator class |
|---|---|
| Euclidean (L2) | vector_l2_ops |
| Inner product | vector_ip_ops |
| Cosine | vector_cosine_ops |
| Hamming | bit_hamming_ops |
Euclidean distance (L2)
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);Use the operator class that matches your vector type. For example, use halfvec_l2_ops for halfvec vectors.
Inner product
CREATE INDEX ON items USING ivfflat (embedding vector_ip_ops) WITH (lists = 100);Cosine distance
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);Hamming distance
CREATE INDEX ON items USING ivfflat (embedding bit_hamming_ops) WITH (lists = 100);Supported vector types:
| Type | Description | Limit |
|---|---|---|
vector | Standard float vectors | Up to 2,000 dimensions |
halfvec | Half-precision vectors | Up to 4,000 dimensions |
bit | Binary vectors | Up to 64,000 dimensions |
Query options
| Parameter | Default | Description |
|---|---|---|
ivfflat.probes | 1 | Number of lists to search. Higher values improve recall but slow down queries. Setting probes equal to lists performs an exact search, but the query planner will not use the index. |
Apply probes to a single query using SET LOCAL:
BEGIN;
SET LOCAL ivfflat.probes = 10;
SELECT ...
COMMIT;Speed up index building
Two parameters control build speed:
| Parameter | Default | Description |
|---|---|---|
maintenance_work_mem | Cluster-dependent | Maximum memory for index building. When the HNSW graph fits within this limit, building is faster. Adjust based on your cluster specifications — set a value that avoids memory exhaustion. |
max_parallel_maintenance_workers | 2 | Number of parallel workers for index creation. To use more workers, first increase max_parallel_workers (default: 8). |
SET max_parallel_maintenance_workers = 7; -- plus leader threadIf maintenance_work_mem is too low for your HNSW index, the database logs:
NOTICE: hnsw graph no longer fits into maintenance_work_mem after 100000 tuples
DETAIL: Building takes significantly more time.
HINT: Increase maintenance_work_mem to speed up building.Creating an index after loading your initial data is faster than building on an empty table (for both index types). Index parameters also significantly affect build time.
Monitor index building progress
Query pg_stat_progress_create_index to track progress during index creation.
HNSW build phases:
initializingloading tuples
IVFFlat build phases:
initializingperforming k-meansassigning tuplesloading tuples
-- HNSW progress
SELECT phase, round(100.0 * blocks_done / nullif(blocks_total, 0), 1) AS "%" FROM pg_stat_progress_create_index;
-- IVFFlat progress
SELECT phase, round(100.0 * tuples_done / nullif(tuples_total, 0), 1) AS "%" FROM pg_stat_progress_create_index;The%column is updated only during theloading tuplesphase.