Skip to main content
View rawEdit

02. Query performance analysis (top-N)

Comprehensive query performance dashboard showing top queries by various metrics.

02. Query analysis dashboard

Purpose

Identify the most resource-intensive queries across multiple dimensions:

  • Call frequency
  • Execution time
  • Planning time
  • Rows processed
  • Shared buffer usage

When to use

  • Regular query performance reviews
  • Investigating slow application responses
  • Finding optimization candidates
  • Before/after index changes

Key panels

Top 10 statements by calls per second

What it shows:

  • Most frequently executed queries
  • Rate of calls over time

Interpretation:

  • High-frequency queries are prime optimization targets
  • Even small improvements have large cumulative impact
  • Sudden spikes may indicate application issues

Top 10 statements by execution time per second

What it shows:

  • Queries consuming the most total execution time
  • Combines frequency × duration

This is often the most important panel — queries here have the highest overall impact on database load.

Execution time per second panel

Interpretation:

  • A query appearing here but not in "calls/sec" = slow but infrequent
  • A query appearing here and in "calls/sec" = high priority for optimization

Top 10 statements by execution time per call

What it shows:

  • Average duration per query execution
  • Identifies slow individual queries

Healthy range:

  • OLTP queries: < 100ms
  • Reporting queries: < 5s (depends on complexity)

Warning signs:

  • Queries over 1s in OLTP workloads
  • Increasing trend over time (regression)

Top 10 statements by planning time

What it shows:

  • Time spent in query planning
  • High values indicate complex query structures

When planning time matters:

  • Very short-lived queries (OLTP)
  • Queries with many JOINs
  • Dynamic SQL with different parameters

Top 10 statements by rows

What it shows:

  • Queries processing the most rows
  • Rows returned vs rows scanned

Warning signs:

  • Large gap between rows scanned and returned = missing indexes
  • Very high row counts = potential full table scans

Top 10 statements by shared buffers

What it shows:

  • Buffer cache hits and reads
  • Memory pressure indicators

Healthy state:

  • High hit ratio (> 99%)
  • Reads should be minimal for hot data

Variables

VariablePurposeOptions
cluster_nameCluster filterYour clusters
node_nameNode filterSpecific nodes
db_nameDatabase filterFilter by database
top_nNumber of queries5, 10, 20, 50
legend_labelQuery display formatSee below

Legend label options

ValueShowsExample
queryidNumeric ID-4021163671685...
displaynameSmart truncationupdate pgbench_ac...
displayname_longFull contextupdate pgbench_accounts set abalance = abalance + $1 where aid = $2
tip

Use displayname_long during debugging to see complete query context.

Detailed table view

Expand the Detailed table view section for a tabular breakdown including:

  • queryid
  • Query text
  • Calls
  • Total time
  • Mean time
  • Rows
  • Hit ratio

Detailed table view

Click any row to drill down to 03. Single query.

Query text integration

The dashboard shows actual query text (not just queryid) by:

  1. Joining metrics with pg_stat_statements via the Flask backend
  2. Smart truncation for readability
  3. Full text available in tooltips and detail view

Troubleshooting

Query texts show as "unknown" or queryid only

  1. Check Flask backend is running:

    curl http://localhost:8000/health
  2. Verify pg_stat_statements has data:

    select count(*) from pg_stat_statements;
  3. Check backend logs:

    docker compose logs monitoring_flask_backend

Metrics don't match pg_stat_statements directly

Dashboard shows rates (per second), not cumulative totals. For raw totals:

select query, calls, total_exec_time
from pg_stat_statements
order by total_exec_time desc
limit 10;

New queries not appearing

pg_stat_statements may need time to accumulate data for new queries. Check:

select pg_stat_statements_reset();  -- caution: resets all stats

Or wait for the next scrape interval (default: 60s).