Approach overview — profile first, then iterate with targeted indexing and query rewrites, measuring changes with reproducible benchmarks.1) Profile & benchmark- Reproduce production-like conditions: copy a representative sample (or use production snapshot anonymized) with same cardinalities.- Capture baseline: run EXPLAIN (ANALYZE, BUFFERS) / STATEMENT on Postgres, or EXPLAIN for other engines; collect runtime, planner node times, I/O, CPU, and row estimates vs actuals.- Use pg_stat_statements / query profiler or cloud query logs to gather variance and percentiles (p50/p95/p99).- Save query plan and runtime as baseline.2) Diagnose common problems- Look for sequential scans on large tables, large nested-loop join with many outer rows, massive temp file usage, or misestimates (rows vs actuals).- Check missing or wrong indexes, outdated statistics (ANALYZE), inappropriate data types, unnecessary DISTINCT/ORDER BY, or SELECT * returning wide rows.3) Indexes and rewrites to try- Add covering index for join/filter columns used in WHERE, JOIN, ORDER BY.Example index (Postgres):sql
CREATE INDEX CONCURRENTLY idx_orders_customer_status_created_at
ON orders (customer_id, status, created_at DESC)
INCLUDE (total_amount);
- If queries filter on a date range and then aggregate, a composite index (customer_id, created_at) helps.- Consider partial indexes for common predicates:sql
CREATE INDEX CONCURRENTLY idx_orders_active_recent
ON orders (customer_id, created_at)
WHERE status = 'active';
- Rewrite: replace correlated subquery with JOIN + aggregation or use window functions to avoid nested loops.Original slow pattern:sql
SELECT c.id, c.name,
(SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.id) AS orders_count
FROM customers c
WHERE c.region = 'EMEA';
Rewritten:sql
SELECT c.id, c.name, COALESCE(o.orders_count,0) AS orders_count
FROM customers c
LEFT JOIN (
SELECT customer_id, COUNT(*) AS orders_count
FROM orders
GROUP BY customer_id
) o ON o.customer_id = c.id
WHERE c.region = 'EMEA';
Or use window if you need per-row enrichment.4) Validation & measurement- Re-run EXPLAIN (ANALYZE, BUFFERS) and compare: total runtime, planning vs execution time, actual vs estimated rows, heap read vs index read, temp file usage.- Use controlled benchmarks: run N cold runs (drop caches) and N warm runs; report median and p95. Measure CPU, I/O, and query duration.- Track business metrics: concurrency behavior under load (use load testing with pgbench or JMeter), and impact on downstream SLAs.5) Additional considerations & trade-offs- Maintainability: too many indexes slow writes; prefer partial/covering indexes.- Stats: run ANALYZE after heavy changes.- Materialized views or pre-aggregation if realtime not required.This systematic loop—profile, hypothesize (index/rewrite), implement, and measure with EXPLAIN + repeatable benchmarks—ensures improvements are real and safe for production.