pgvector adds vector search to PostgreSQL, but running it in production without monitoring index health, query latency, or embedding dimension drift creates blind spots that surface only after users complain. A misconfigured HNSW index can silently slow down queries by 10x. An IVFFlat index with too few lists can tank recall to 60% without firing a single alert. And high dimensional embeddings over 1,000 dimensions often fail to index entirely without any visible error, leaving vector search broken until someone manually checks.
This guide covers how to monitor pgvector in production with query performance metrics, index health tracking, and real telemetry showing when HNSW builds stall, when query latency spikes, and when distance function choice is hurting recall.
What Is pgvector Query Performance Monitoring
pgvector query performance monitoring is the practice of tracking how vector similarity searches perform in PostgreSQL deployments using the pgvector extension. It measures query latency, index build progress, recall accuracy, and resource consumption to identify bottlenecks before they impact application performance or search quality.
Unlike standard PostgreSQL monitoring that tracks transaction throughput and table scans, pgvector monitoring focuses on signals specific to approximate nearest neighbor (ANN) search, including HNSW graph completeness, IVFFlat centroid distribution, embedding dimension consistency, and distance function execution time. These signals determine whether vector search is fast, accurate, and stable at production scale.
Without pgvector specific monitoring, teams often discover issues only after query latency degrades visibly or users report irrelevant search results. A typical scenario: an HNSW index builds successfully but query performance remains slow because ef_search was left at the default value of 40, which is too low for high recall workloads. Standard Postgres slow query logs surface the symptom but do not identify the root cause, HNSW tuning parameters that no one tracked during deployment.
Monitoring pgvector means tracking two layers together: the PostgreSQL query layer where you see execution time and index scans, and the pgvector internals layer where index type, build state, and ANN algorithm parameters determine whether queries return the right results fast enough.
Why pgvector Query Performance Degrades in Production
pgvector query performance problems in production typically stem from five areas: index configuration mismatches with workload characteristics, high dimensional embeddings that PostgreSQL cannot efficiently store or search, query parameter tuning left at unsafe defaults, resource contention during index builds, and distance function choices that do not match the embedding model’s output distribution.
Index type mismatch is the most common issue. IVFFlat builds fast and uses less memory but degrades in accuracy and speed as dataset size grows beyond a few hundred thousand vectors. HNSW provides better recall and query speed at large scale but requires significantly more memory and longer build times. Teams often start with IVFFlat in development where the dataset is small, then deploy to production with 10 million vectors without switching to HNSW. The result: queries slow down linearly as the table grows, and recall drops to 70% or lower because IVFFlat with 1,000 lists cannot partition a 10 million row vector space effectively.
High dimensional embeddings create storage and indexing bottlenecks that are invisible until index creation fails. PostgreSQL stores data in 8KB pages. Each vector dimension uses 4 bytes as a float, plus metadata overhead. A 2,048 dimension embedding requires over 8KB per row, which means it cannot fit in a single page. pgvector does not reject the insert, but indexing performance collapses, and in many cases HNSW index builds silently fail partway through, leaving the index incomplete. Without monitoring index build progress and dimension cardinality, this goes unnoticed until someone runs a vector search and gets zero results.
Query parameter defaults are set conservatively for safety, not performance. HNSW’s default ef_search is 40, which ensures low latency but sacrifices recall. IVFFlat’s default probes is 1, which means the query checks only one partition and misses most relevant vectors. Production workloads often need ef_search between 200 and 400, and probes between 10 and 50, depending on the recall target. Teams that deploy pgvector without tuning these parameters see queries complete in milliseconds but return poor results, and they often blame the embedding model rather than the query configuration.
Resource contention during index builds causes production incidents that look like unrelated database slowness. Building an HNSW index on a large table with m=16 and ef_construction=256 can consume 80% of available memory and saturate CPU for hours. If the build runs during peak traffic, it starves application queries of resources. Worse, if the build fails partway through due to an out of memory error, PostgreSQL rolls back the index creation but does not log why it failed. Without monitoring memory usage and build progress metrics, teams retry the same build repeatedly, hitting the same resource limits each time.
Distance function choice affects both speed and correctness in ways that only monitoring surfaces. Cosine similarity is the most common choice for text embeddings, but pgvector implements it as 1 - (a <=> b), which means it calculates inner product then subtracts from 1. If embeddings are not normalized to unit length, cosine distance returns incorrect results. Inner product (<#>) is faster but only correct for normalized embeddings. L2 distance (<->) works for any embedding but runs slower on high dimensional data. Monitoring query execution time by distance function reveals when the wrong operator is being used, but only if you track it explicitly.
How pgvector Query Performance Monitoring Works
pgvector query performance monitoring works by collecting metrics from three sources: PostgreSQL’s query execution stats, pgvector internal index metadata, and application level query latency measurements. These signals are correlated to identify whether performance problems originate from query planning, index configuration, or resource exhaustion.
PostgreSQL’s pg_stat_statements extension surfaces query execution time, index scans, and sequential scans for every query pattern. For pgvector queries, this shows how long each vector similarity search takes and whether it used the expected index. A query that scans the entire table instead of using the HNSW index indicates either a missing or broken index, or query parameters that bypass index usage. Monitoring pg_stat_statements filtered for queries using <->, <#>, or <=> operators isolates pgvector specific performance.
pgvector stores index metadata in pg_class and pg_index, including index size, build status, and configuration parameters. Querying these tables reveals whether an HNSW index build completed, how many pages it occupies, and what m and ef_construction values were used during creation. An index marked as valid but occupying zero pages indicates a failed build that PostgreSQL did not report as an error. Tracking index size over time shows whether the index is growing proportionally with the table or falling behind, which happens when concurrent inserts outpace index maintenance.
Application level query latency measurements capture the end to end user experience, including network round trip time and connection pooling overhead that PostgreSQL internal metrics miss. An application instrumented with infrastructure monitoring can correlate slow pgvector queries with resource usage spikes, showing whether high CPU from index maintenance caused query slowdown. This correlation is critical because PostgreSQL query stats show only database internal execution time, not whether the database was starved of resources by a concurrent index build.
Distance function execution time is tracked separately by running EXPLAIN ANALYZE on representative queries and extracting the operator specific cost. pgvector’s vector distance operators have different performance characteristics depending on dimensionality and index type. Monitoring execution time for <-> vs <#> vs <=> across different dimension sizes reveals which operator is most efficient for your embedding model. A query using cosine distance <=> on 1,536 dimension embeddings might run 2x slower than inner product <#> on the same data, even though both return similar results if embeddings are normalized.
Index health metrics include HNSW graph connectivity, IVFFlat centroid balance, and build progress percentage. For HNSW, connectivity is measured by querying internal graph statistics to verify that every node has approximately m edges. Low connectivity indicates a corrupted index or an interrupted build. For IVFFlat, centroid balance is checked by running a test query with probes set to 1 and measuring how many results come from the same partition. If 90% of results are from one partition, the centroid distribution is skewed and recall will degrade.
Embedding dimension drift is monitored by tracking the vector column’s average dimension count over time using a custom query. pgvector allows variable dimension vectors in the same table, which is a footgun. If an application bug starts inserting 768 dimension embeddings into a table configured for 1,536 dimensions, queries return partial results without any error. Monitoring dimension cardinality per inserted batch surfaces this immediately.
Key Metrics for pgvector Query Performance Monitoring
Monitoring pgvector query performance requires tracking nine core metrics: query latency by distance function, index scan vs sequential scan ratio, HNSW index build progress, IVFFlat centroid utilization, embedding dimension consistency, index size growth rate, memory usage during index builds, recall accuracy on test queries, and query throughput under load.
Query latency by distance function is the most direct performance signal. Measure p50, p95, and p99 latency separately for queries using L2 distance <->, inner product <#>, and cosine similarity <=>. High p99 latency indicates that some queries are hitting edge cases, such as searching for an outlier vector that requires checking many nodes in the HNSW graph. Latency that increases linearly with table size suggests the index is not being used or is misconfigured.
Index scan vs sequential scan ratio reveals whether queries are using the index as expected. A pgvector query should always use an index scan if an HNSW or IVFFlat index exists and is valid. A sequential scan means PostgreSQL decided the index was too expensive to use, which happens when ef_search or probes are set so high that checking the index costs more than scanning the entire table. Monitor this ratio per query type, if it drops below 95%, investigate index configuration and query parameters.
HNSW index build progress is tracked by monitoring pg_stat_progress_create_index, which shows how many blocks have been processed during index creation. HNSW builds are slow and memory intensive. A build that stalls at 60% completion indicates an out of memory condition or a corrupted table. Monitoring build progress in real time allows canceling and restarting with adjusted memory limits before hours of work are wasted.
IVFFlat centroid utilization measures how evenly vectors are distributed across partitions. Query pg_stats after running ANALYZE to get the distribution of vectors per list. Ideally, each list should contain roughly table_size / lists vectors. If one list contains 50% of vectors, IVFFlat performance degrades because every query must scan that list regardless of probes setting. This indicates either a poor choice of lists parameter or non-uniform embedding distribution.
Embedding dimension consistency is verified by running a periodic query that checks the dimension count of every vector in the table. pgvector allows variable dimensions, but performance and correctness require consistency. A query like SELECT DISTINCT array_length(embedding::float[], 1) FROM table should return a single value. Multiple values indicate a data ingestion bug. Monitoring this daily catches dimension drift before it corrupts search results.
Index size growth rate is calculated by tracking pg_class.relpages for the index over time and comparing it to table row count growth. HNSW indexes grow non-linearly because each new vector adds edges to the graph, increasing index size faster than the table itself. If index size stops growing while the table continues to grow, the index is stale and needs rebuilding. If index size grows faster than expected, m may be set too high, causing memory waste.
Memory usage during index builds is monitored by tracking work_mem and maintenance_work_mem consumption via pg_stat_activity while the index build is running. HNSW builds can exceed maintenance_work_mem, causing PostgreSQL to spill to disk, which slows the build by 10x or more. Monitoring memory usage in real time allows increasing maintenance_work_mem before starting the build, or scheduling builds during off peak hours when more memory is available.
Recall accuracy on test queries is measured by running a known set of vector queries with ground truth results and comparing pgvector’s returned results to the expected top k neighbors. Recall is calculated as |returned ∩ expected| / k. High recall (95% or above) confirms the index is working correctly. Recall below 90% indicates either incorrect distance function choice, insufficient ef_search or probes, or corrupted index data. This metric is the only way to verify search quality, query latency alone does not reveal accuracy problems.
Query throughput under load is tested by running concurrent pgvector queries and measuring how many queries per second the database can sustain before latency degrades. Use a load testing tool to simulate production traffic patterns and track latency at 10, 50, 100, and 200 concurrent queries. Throughput that plateaus or drops at high concurrency indicates lock contention, insufficient connection pooling, or resource exhaustion. This metric reveals production capacity limits before they impact users.
Distance Function Analysis for Performance Optimization
Distance function choice directly impacts both query speed and result correctness, but the optimal choice depends on whether embeddings are normalized, the dimensionality of vectors, and the underlying embedding model’s output distribution. Monitoring execution time and recall across all three pgvector distance operators reveals which one performs best for your data.
L2 distance (Euclidean distance), represented by the <-> operator, calculates the straight line distance between two vectors. It works correctly for any embedding, normalized or not, but runs slower on high dimensional data because it computes the square root of the sum of squared differences. For 1,536 dimension embeddings, L2 distance queries often run 1.5x to 2x slower than inner product queries on the same data. Use L2 when embeddings are not normalized and correctness is more important than speed.
Inner product, represented by the <#> operator, calculates the dot product of two vectors. It is the fastest distance function pgvector offers because it skips the square root computation. However, it only returns correct similarity rankings if embeddings are normalized to unit length. If embeddings are not normalized, inner product returns meaningless results. Monitor embedding vector magnitudes by sampling vectors and calculating their L2 norm, if most are close to 1.0, embeddings are normalized and inner product is safe to use.
Cosine similarity, represented by the <=> operator, is implemented as 1 - (a <#> b) / (||a|| * ||b||). pgvector internally normalizes vectors before computing inner product. This makes it slower than inner product but safer for unnormalized embeddings. However, if embeddings are already normalized, cosine similarity wastes CPU cycles re normalizing them. Measure query execution time for cosine vs inner product on your dataset, if they are identical, embeddings are normalized and switching to inner product provides free speedup.
Monitoring execution time by distance function requires running EXPLAIN ANALYZE on representative queries and extracting the operator cost. Create a test query that searches for the nearest 10 neighbors using each distance function and compare total execution time. If L2 and cosine return identical latency but inner product is 30% faster, embeddings are normalized and inner product should be used. If cosine is significantly slower than L2, check whether embeddings are already normalized, if so, cosine normalization is redundant.
Recall differences across distance functions surface when the wrong operator is used for the embedding model. Some embedding models output vectors optimized for cosine similarity, others for inner product. Using the wrong distance function can reduce recall by 10% to 20% even if queries complete successfully. Run recall tests using all three operators with the same test set and compare results. If cosine returns 95% recall but inner product returns 80% recall, the model outputs unnormalized embeddings and cosine is required for correctness.
High dimensional embeddings amplify performance differences between distance functions. At 2,048 dimensions, L2 distance can be 3x slower than inner product. At 768 dimensions, the difference is negligible. Monitor query latency as dimension count increases. If latency grows non linearly, consider quantization or switching to a faster distance function. pgvector 0.7.0+ supports scalar quantization (half precision floats) and binary quantization, both of which reduce computation cost for high dimensional searches.
HNSW Index Monitoring: Build Progress, Graph Health, and Query Parameters
HNSW (Hierarchical Navigable Small World) is pgvector’s most performant index type for large datasets, but it is also the most complex to configure and monitor. HNSW index health depends on build completion, graph connectivity, memory usage, and query time parameters that determine the speed vs recall tradeoff.
HNSW index build progress is monitored using PostgreSQL’s pg_stat_progress_create_index view, which shows how many heap blocks have been processed during index creation. For a table with 1 million rows, an HNSW build might take 20 to 60 minutes depending on m and ef_construction settings. A build that stalls at 40% completion for more than 10 minutes indicates a resource bottleneck, typically memory or CPU saturation. Monitor pg_stat_activity during the build to confirm the process is still active and not waiting on a lock.
HNSW graph connectivity determines query performance and recall. Each vector in the graph should have approximately m bidirectional edges to neighboring vectors. Low connectivity, where some nodes have fewer than m/2 edges, indicates a corrupted index or an interrupted build. pgvector does not expose internal graph statistics directly, but you can infer connectivity problems by running test queries with varying ef_search values. If increasing ef_search from 100 to 400 does not improve recall, the graph is likely disconnected or incomplete.
Memory usage during HNSW builds is the most common production issue. Building an HNSW index on a table with 10 million 1,536 dimension vectors and m=16 can require 80GB of memory or more. PostgreSQL’s maintenance_work_mem parameter controls how much memory is available for index builds. If this is set too low, the build spills to disk, slowing it by 10x. Monitor memory usage in real time via pg_stat_activity and system metrics. If memory usage exceeds maintenance_work_mem, increase it before starting the next build.
ef_construction is the HNSW build time parameter that controls graph quality. Higher values create a more connected graph, improving recall but significantly increasing build time and memory usage. ef_construction=64 builds fast but may produce a graph with poor connectivity. ef_construction=256 or ef_construction=512 produces a high quality graph but can take 3x to 5x longer to build. Monitor build time and recall accuracy together, if recall is below 95%, increase ef_construction. If build time is unacceptable, lower it and compensate by increasing ef_search at query time.
ef_search is the HNSW query time parameter that controls how many nodes the algorithm explores when searching for neighbors. Higher values improve recall at the cost of latency. pgvector’s default ef_search=40 is too low for most production workloads. Teams targeting 95% recall typically need ef_search between 200 and 400. Monitor query latency at different ef_search values by running test queries with SET hnsw.ef_search = <value> and measuring execution time. Plot latency vs recall to find the optimal value for your workload.
HNSW index size growth is monitored by tracking pg_class.relpages for the index over time. HNSW indexes grow faster than the underlying table because each new vector adds edges to the graph. A 10 million row table might produce a 50GB HNSW index with m=16. If index size stops growing while rows continue to be inserted, the index is stale and needs rebuilding. If index size grows disproportionately, m may be set too high, wasting memory without improving recall.
IVFFlat Index Monitoring: Centroid Balance, Probe Tuning, and Build Efficiency
IVFFlat (Inverted File with Flat quantization) is pgvector’s simpler index type, faster to build and lighter on memory than HNSW, but with lower recall and performance that degrades as dataset size grows. Monitoring IVFFlat requires tracking centroid distribution, probe count effectiveness, and build efficiency.
IVFFlat partitions the vector space into lists clusters, each represented by a centroid vector. At query time, pgvector searches only the probes nearest centroids, ignoring the rest. If centroids are poorly distributed, most vectors end up in a few clusters, and recall suffers. Monitor centroid balance by running ANALYZE on the table, then querying pg_stats to see the distribution of vectors per list. Ideally, each list should contain roughly table_size / lists vectors. If one list contains more than 20% of vectors, increase lists and rebuild the index.
lists is the build time parameter that determines how many partitions IVFFlat creates. pgvector recommends lists = rows / 1000 as a starting point, so a 1 million row table should use lists=1000. Too few lists causes poor centroid distribution and slow queries. Too many lists increases build time and memory usage without improving recall. Monitor query latency and recall at different lists values by rebuilding the index with different settings and running test queries. If recall plateaus at 85% no matter how many lists you use, IVFFlat cannot handle the dataset size and you need to switch to HNSW.
probes is the query time parameter that controls how many centroids are searched. Higher values improve recall but increase latency linearly. pgvector’s default probes=1 searches only the single nearest centroid, which almost always produces poor recall. Teams targeting 90% recall typically need probes between 10 and 50. Monitor query latency at different probes values by running test queries with SET ivfflat.probes = <value> and measuring execution time. Plot latency vs recall to find the optimal value.
IVFFlat build time is significantly faster than HNSW. A 1 million row table with lists=1000 might build in 2 to 5 minutes compared to 30 minutes for HNSW. However, IVFFlat build time increases linearly with lists, so setting lists=10000 can make builds as slow as HNSW without the same performance benefits. Monitor build time via pg_stat_progress_create_index. If builds take longer than 10 minutes, either reduce lists or switch to HNSW for better query performance.
IVFFlat index size is smaller than HNSW for the same dataset because it stores only centroids and partition assignments, not a full graph. A 10 million row table might produce a 10GB IVFFlat index compared to a 50GB HNSW index. Monitor index size growth over time, if it grows disproportionately fast, lists may be set too high. If index size stops growing while rows are inserted, the index is stale.
IVFFlat performance degrades as dataset size increases because partition size grows and probe cost increases linearly. A table with 1 million rows and lists=1000 has partitions of ~1,000 vectors each. At 10 million rows with the same lists, partitions grow to 10,000 vectors each, and searching each partition takes 10x longer. Monitor query latency as table size grows. If latency increases linearly with row count, IVFFlat is no longer suitable and HNSW should be used.
Tools and Implementation: Monitoring pgvector in Production
Monitoring pgvector query performance in production requires instrumenting PostgreSQL with telemetry collection, query logging, and resource usage tracking, then correlating these signals with application level metrics to identify the root cause of performance degradation.
PostgreSQL’s pg_stat_statements extension is the first tool to enable. It tracks execution time, calls, and rows returned for every unique query pattern. Enable it by adding shared_preload_libraries = 'pg_stat_statements' to postgresql.conf, then restart PostgreSQL. Query pg_stat_statements filtered for pgvector operators (<->, <#>, <=>) to isolate vector search performance. Track mean execution time, p95 latency, and calls per minute for each query type.
EXPLAIN ANALYZE is used to debug individual slow queries. Run EXPLAIN ANALYZE SELECT * FROM table ORDER BY embedding <-> '[query_vector]' LIMIT 10; to see whether the query uses an index scan or sequential scan, and how much time is spent in each operator. If the plan shows a sequential scan, the index is either missing, invalid, or the query planner decided it was too expensive to use. Check pg_index.indisvalid to confirm index status.
CubeAPM provides unified monitoring for pgvector deployments by collecting PostgreSQL query metrics, correlating them with application performance data, and surfacing index health issues before they impact users. It tracks HNSW and IVFFlat index build progress, query latency by distance function, and embedding dimension drift in a single dashboard. CubeAPM’s self hosted deployment ensures sensitive embedding data never leaves your infrastructure, making it suitable for teams with data residency or compliance requirements. Pricing is $0.15/GB of telemetry ingested, with unlimited retention and no per seat fees.
Prometheus combined with the postgres_exporter surfaces PostgreSQL resource usage metrics including memory, CPU, disk I/O, and active connections. Configure postgres_exporter to scrape pg_stat_statements and pg_stat_progress_create_index to track pgvector specific metrics. Grafana dashboards can visualize query latency, index build progress, and memory usage over time. This stack is suitable for teams already using Prometheus, but requires manual dashboard configuration and does not correlate PostgreSQL metrics with application level telemetry.
Datadog APM integrates with PostgreSQL via the Datadog Agent’s postgres check, which collects query metrics and resource usage. Enable the pg_stat_statements integration to track query execution time per statement. Datadog surfaces slow queries automatically and can correlate them with infrastructure metrics like CPU and memory usage. However, Datadog’s per host pricing scales unpredictably as monitoring coverage expands, and its cloud only architecture sends all telemetry outside your infrastructure. Pricing starts at $15/host/month for infrastructure monitoring, with APM adding another $31/host/month.
New Relic provides PostgreSQL monitoring through its infrastructure agent, which collects query performance, slow query logs, and database metrics. New Relic’s pricing model charges per user ($49 to $99/user/month) and per GB of data ingested ($0.35/GB), making it expensive at scale. It does not provide pgvector specific monitoring out of the box, so you must manually configure custom queries to track index health and query parameters.
Application level instrumentation using OpenTelemetry captures query latency from the perspective of the application, including network round trip time and connection pooling overhead. Instrument database query spans to include the distance function used, ef_search or probes value, and number of results returned. This allows correlating slow queries with specific index configurations and tuning parameters. Platforms that support OpenTelemetry natively, like CubeAPM, can visualize this data without additional configuration.
Best Practices for pgvector Query Performance Monitoring
Monitoring pgvector effectively requires instrumenting every layer of the stack, PostgreSQL internals, pgvector specific index health, query parameters, and application level latency, then correlating these signals to identify root causes before performance degrades visibly.
Track query latency per distance function separately. Do not aggregate L2, inner product, and cosine similarity queries into a single latency metric. Each operator has different performance characteristics. Create separate dashboards or alerts for each distance function to identify which one is causing slowdowns. If cosine similarity p99 latency spikes to 500ms while inner product remains at 50ms, the application is using the wrong operator for normalized embeddings.
Monitor HNSW index build progress during every index creation. Do not rely on the CREATE INDEX command completing successfully. Query pg_stat_progress_create_index in real time to confirm the build is progressing. Set an alert if build progress stalls for more than 10 minutes. Many HNSW builds fail silently due to out of memory conditions, leaving an incomplete index marked as valid.
Set explicit ef_search values in production queries. Do not rely on pgvector’s default ef_search=40. Set it explicitly per query based on your recall target. Monitor recall accuracy on a test dataset daily and adjust ef_search if recall drops below your threshold. If you need 95% recall, you likely need ef_search between 200 and 400.
Test IVFFlat centroid distribution after every major data load. Run ANALYZE then query pg_stats to check vector distribution across lists. If more than 20% of vectors are in one list, rebuild the index with more lists. Skewed centroid distribution causes IVFFlat performance to degrade unpredictably.
Monitor embedding dimension consistency daily. Query SELECT DISTINCT array_length(embedding::float[], 1) FROM table and alert if more than one dimension value is returned. Variable dimensions cause incorrect query results without any error message. A data ingestion bug that inserts 768 dimension embeddings into a 1,536 dimension table will silently break vector search.
Track index size growth rate relative to table growth. Calculate index_size / table_size weekly and alert if the ratio changes significantly. If index size stops growing while rows are inserted, the index is stale. If index size grows faster than expected, m or lists may be set too high.
Use recall accuracy tests as the primary signal of index health. Query latency alone does not reveal whether search results are correct. Maintain a test dataset with known ground truth neighbors and run recall tests daily. If recall drops below 90%, investigate index configuration and query parameters before users report poor search quality.
Disclaimer: The information in this article reflects the latest details available at the time of publication and may change as technologies and products evolve. Features, pricing, and plan limits can change over time. Always verify the latest information directly with the vendor before making purchasing or deployment decisions.
Frequently Asked Questions
What is pgvector and why does it need monitoring?
pgvector is a PostgreSQL extension that adds vector similarity search for embeddings. It needs monitoring because index configuration, query parameters, and embedding dimensions directly impact search speed and accuracy in ways that are not visible through standard PostgreSQL metrics.
What metrics should I monitor for pgvector query performance?
Monitor query latency by distance function, index scan vs sequential scan ratio, HNSW or IVFFlat build progress, embedding dimension consistency, index size growth, memory usage during builds, recall accuracy on test queries, and query throughput under load.
How do I know if my HNSW index is working correctly?
Verify that the index build completed by checking `pg_stat_progress_create_index`, run test queries with varying `ef_search` values and measure recall accuracy, and confirm that queries use index scans rather than sequential scans via `EXPLAIN ANALYZE`.
What is the difference between HNSW and IVFFlat indexes in pgvector?
HNSW builds a navigable graph for fast approximate nearest neighbor search, providing better recall and query speed at large scale but requiring more memory and build time. IVFFlat partitions vectors into clusters, building faster with less memory but degrading in accuracy and speed as dataset size grows.
How do I optimize pgvector query performance?
Choose the right index type for your dataset size, tune HNSW `ef_search` or IVFFlat `probes` based on your recall target, select the correct distance function for your embeddings, monitor index health and rebuild when necessary, and ensure embeddings are normalized if using inner product or cosine similarity.
What tools can monitor pgvector in production?
PostgreSQL `pg_stat_statements` tracks query execution time, `EXPLAIN ANALYZE` debugs individual slow queries, Prometheus with `postgres_exporter` surfaces resource usage, and platforms like CubeAPM provide unified observability across PostgreSQL metrics, application traces, and index health.
How does distance function choice affect pgvector performance?
L2 distance works for any embedding but runs slower on high dimensions, inner product is fastest but only correct for normalized embeddings, and cosine similarity normalizes vectors before computing inner product, making it slower than inner product if embeddings are already normalized.





