Best Practices for PostgreSQL Vector Search

Last verified 13 Jul 2026

DigitalOcean Managed PostgreSQL for vector search uses the same managed PostgreSQL engine available under Managed Databases, with the pgvector and pgvectorscale extensions for storing and querying vector embeddings alongside relational data.

PostgreSQL vector search uses the pgvector extension to store embeddings and compare them by similarity. After you load embeddings into a table, choose a distance operator, index type, and tuning strategy based on your data size, query patterns, recall requirements, and latency goals.

These choices affect search quality, query speed, index build time, and memory usage.

Use this guide to choose between exact search, HNSW, IVFFlat, filters, partial indexes, reranking, and query-time tuning. For larger architectures that need pgvectorscale, disk-resident search, hybrid retrieval, RAG, or read scaling, see Best Practices for Advanced PostgreSQL Vector Workloads.

Use Vector Search for Semantic Matching

Use vector search when users search by meaning instead of exact words.

Vector search is a good fit for:

  • Natural-language questions.
  • Semantic document search.
  • Recommendations.
  • Similarity matching.
  • Retrieval for RAG applications.
  • Queries where users may describe the same idea with different words.

Vector search is less effective when queries depend on exact tokens, such as product codes, commands, filenames, IDs, or error messages. For those workloads, combine vector search with PostgreSQL full-text search using hybrid search.

Choose a Distance Operator

A distance operator controls how PostgreSQL compares a query vector to the vectors stored in your table.

Choose the operator that matches your embedding model, and use it consistently in your queries and indexes.

Common pgvector distance operators include:

Distance Type Operator Use Case
Cosine distance <=> Most text embedding models
Negative inner product <#> Normalized vectors or models trained for inner product
L2 distance <-> Euclidean distance, often for image or general embeddings
L1 distance <+> Manhattan distance, less common

Cosine distance is a good default for most text embedding workloads. It measures the angle between vectors, which works well when vector direction matters more than magnitude.

The distance operator, index operator class, and ORDER BY clause must match. For example, use <=> with vector_cosine_ops.

Choose the Simplest Search Pattern First

Start with the simplest search pattern that meets your recall, latency, and scale requirements.

PostgreSQL vector search generally fits into three standard pgvector patterns:

  • Exact search: Scans matching rows and computes vector distance exactly. Use it for small datasets, small filtered candidate sets, or validation tests.
  • HNSW search: Uses a graph index for fast approximate nearest-neighbor search. Use HNSW as the default for most small-to-medium production workloads.
  • IVFFlat search: Uses list-based partitioning for faster index builds and lower memory usage when you can accept lower recall.

Avoid starting with the most complex option. Use exact search or HNSW first, measure performance, and move to IVFFlat only when build speed, ingest cost, or memory usage matters more than recall.

If standard pgvector indexes no longer meet your scale or memory requirements, evaluate pgvectorscale in Best Practices for Advanced PostgreSQL Vector Workloads.

Use Exact Search for Small Tables

Without a vector index, PostgreSQL performs an exact nearest-neighbor scan. It compares the query vector against every matching row, then returns the closest matches.

Exact search is simple and returns the most accurate results, but it gets slower as the table grows.

Use exact search when:

  • Your table is small.
  • Filters reduce the candidate set to a small number of rows.
  • You need a baseline to compare approximate index results against.
  • You are testing a new embedding model or ranking strategy.

For larger datasets, use an approximate nearest-neighbor index.

Use Approximate Indexes for Larger Tables

Approximate nearest-neighbor indexes trade a small amount of recall for faster search. In pgvector, the two main approximate index types are HNSW and IVFFlat.

Use an approximate index when exact scans are too slow or when your table is large enough that comparing every vector for every query becomes expensive.

Choose HNSW for Most Workloads

HNSW, or Hierarchical Navigable Small World, builds a graph of vectors. Queries traverse the graph to find nearby vectors instead of scanning the whole table.

Use HNSW for most PostgreSQL vector search workloads. It usually provides high recall and fast query performance, but it uses more memory and takes longer to build than IVFFlat.

HNSW is a good fit when:

  • You need strong recall.
  • You need low query latency.
  • You can allocate enough memory for the index.
  • You do not rebuild the index frequently.

HNSW does not require a training step, but for large imports, bulk-load your data before creating the index. Building the index after loading data is usually faster than maintaining the index during many individual inserts.

Use IVFFlat for Faster Builds or Lower Memory

IVFFlat partitions the vector space into lists. At query time, PostgreSQL searches only the most relevant lists.

Use IVFFlat when you need faster index builds or lower memory usage and can accept lower recall unless you increase query-time probes.

IVFFlat is a good fit when:

  • You rebuild indexes often.
  • You have high ingest volume.
  • You want lower memory usage than HNSW.
  • You can tune lists and probes for your workload.

For IVFFlat, load data before creating the index. IVFFlat chooses list centroids from the data available when the index is built. If your data distribution changes significantly, rebuild the index.

Match Operator Classes to Distance Operators

When you create a vector index, choose the operator class that matches your distance operator:

  • Use <=> with vector_cosine_ops for cosine distance.
  • Use <#> with vector_ip_ops for inner product distance.
  • Use <-> with vector_l2_ops for L2 distance.
  • Use <+> with vector_l1_ops for L1 distance.

If the operator class and query operator do not match, PostgreSQL may not use the vector index.

Tune HNSW Parameters

HNSW has index-time and query-time parameters that control recall, latency, memory usage, and build time.

In general, tune HNSW parameters based on the tradeoff you want to make:

  • Increase m when you need better graph connectivity and can accept higher memory usage and slower indexing.
  • Increase ef_construction when you need a higher-quality graph and can accept slower index builds.
  • Increase ef_search when you need higher recall and can accept higher query latency.

Start with the default values, then test with representative queries. Increase one parameter at a time so you can measure which change improves results.

Tune IVFFlat Parameters

IVFFlat uses lists at index build time and probes at query time.

In general, tune IVFFlat parameters based on the tradeoff you want to make:

  • Increase lists when you have more rows and want finer partitions.
  • Increase probes when you need higher recall and can accept higher query latency.

A common starting point for lists is rows / 1000 for fewer than one million rows, or sqrt(rows) for one million rows or more. A common starting point for probes is sqrt(lists).

Test these values with your own data. The best settings depend on vector dimensions, table size, filters, and acceptable latency.

Combine Vector Search with SQL Filters

One advantage of PostgreSQL vector search is that you can combine similarity search with ordinary SQL filters.

Use filters for fields like tenant ID, document type, creation date, language, status, or access permissions. For example, a query can search for the nearest vectors within one tenant or within recently created documents.

Add regular PostgreSQL indexes, such as B-tree or partial indexes, for columns you filter on frequently. This helps PostgreSQL narrow the candidate set before or during vector search.

Use Partial Indexes for Hot Segments

Use partial vector indexes when a specific tenant, category, or segment receives heavy search traffic.

A partial index only includes rows that match a condition. This can make the index smaller, faster to search, and cheaper to maintain.

Partial indexes are useful for multi-tenant workloads where one tenant is much larger or more active than others.

Rerank for Higher Precision

For higher precision, retrieve a larger candidate set first, then rerank those candidates with another scoring method.

This is useful when approximate search is fast enough for candidate retrieval but you need more precise final ordering. For example, you can retrieve the top 50 candidates with vector search, then rerank the final 10 with a different distance operator, a cross-encoder, business logic, or access-control rules.

Maintain Vector Indexes

Keep PostgreSQL statistics and vector indexes current as your data changes.

Run ANALYZE after large inserts so the query planner has current table statistics. Rebuild indexes if you change embedding models, change vector dimensions, or see degraded recall after major data distribution changes.

Vector indexes can use significant memory, especially HNSW indexes. If query latency increases because the index no longer fits comfortably in memory, resize the cluster or consider a lower-dimensional embedding model.

Troubleshoot Slow or Low-Quality Results

If vector search is slow or results look incorrect, check the common causes first:

  1. Confirm the vector index is being used with EXPLAIN (ANALYZE, BUFFERS).
  2. Make sure the distance operator matches the index operator class.
  3. Make sure the query uses ORDER BY embedding <operator> $1 with a LIMIT.
  4. Increase hnsw.ef_search or ivfflat.probes if recall is too low.
  5. Check memory pressure if queries become slow under load.
  6. Rebuild the index if the embedding model or data distribution changed.

We can't find any results for your search.

Try using different keywords or simplifying your search terms.