Query Profiling Across Database Engines
Use EXPLAIN ANALYZE, pg_stat_statements, Performance Schema, and Query Store to find and fix slow queries.
๐ฏ What You'll Learn
- PostgreSQL EXPLAIN with ANALYZE, BUFFERS, and VERBOSE
- pg_stat_statements for finding top resource consumers
- MySQL Performance Schema and slow query log
- SQL Server Query Store and DMVs
- A systematic 7-step profiling workflow
๐ฌ PostgreSQL EXPLAIN Deep Dive
EXPLAIN ANALYZE is your most powerful diagnostic tool. Adding BUFFERS shows I/O details, and VERBOSE shows column-level information. Think of it as a medical scan for your query.
๐ก Pro Tip โ actual rows vs planned rows
If actual rows are 100x different from planned rows, the optimizer made a bad plan due to stale statistics. Run ANALYZE tablename; to refresh.
EXPLAIN ANALYZE
Full diagnostic output with buffers and timing
-- PostgreSQL: EXPLAIN ANALYZE with all options
-- Full diagnostic output:
EXPLAIN (ANALYZE, BUFFERS, TIMING, VERBOSE, FORMAT TEXT)
SELECT c.name, COUNT(o.id) AS order_count, SUM(o.total) AS total_spent
FROM customers c
JOIN orders o ON c.id = o.customer_id
WHERE o.order_date >= '2024-01-01'
GROUP BY c.name
HAVING SUM(o.total) > 1000
ORDER BY total_spent DESC
LIMIT 20;
-- Key metrics to read:
-- actual time=0.015..45.230 (startup..total in ms)
-- rows=20 (actual rows retu
...๐ pg_stat_statements
pg_stat_statements aggregates statistics across all executions of each query pattern. It tells you which queries consume the most total time โ even if each individual execution is fast.
pg_stat_statements
Find top resource-consuming queries
-- pg_stat_statements: find your slowest queries
-- (Must be enabled in postgresql.conf)
-- shared_preload_libraries = 'pg_stat_statements'
SELECT
LEFT(query, 80) AS query_preview,
calls,
ROUND(total_exec_time::numeric / 1000, 2) AS total_seconds,
ROUND(mean_exec_time::numeric, 2) AS avg_ms,
ROUND(max_exec_time::numeric, 2) AS max_ms,
rows AS total_rows,
ROUND((shared_blks_hit::decimal /
NULLIF(shared_blks_hit + shared_blks_read, 0)) * 100, 1)
AS cach
...๐ฌ MySQL Profiling
MySQL uses Performance Schema and the slow query log. MySQL 8.0+ supports EXPLAIN ANALYZE similar to PostgreSQL.
MySQL Profiling
Performance Schema, slow log, and EXPLAIN
-- MySQL: Performance Schema and EXPLAIN ANALYZE
-- Enable performance monitoring:
-- performance_schema = ON (in my.cnf)
-- MySQL 8.0+ EXPLAIN ANALYZE (actually runs the query):
-- EXPLAIN ANALYZE
-- SELECT c.name, COUNT(*) FROM customers c
-- JOIN orders o ON c.id = o.customer_id
-- GROUP BY c.name LIMIT 10;
-- MySQL slow query log:
-- slow_query_log = ON
-- slow_query_log_file = /var/log/mysql/slow.log
-- long_query_time = 1 (log queries > 1 second)
-- Find slow queries in Performance S
...๐ท SQL Server Query Store
SQL Server's Query Store automatically captures execution plans and runtime statistics over time, making it easy to find regressions and compare plan changes.
SQL Server Query Store
Query Store, DMVs, and missing index suggestions
-- SQL Server: Query Store and Execution Plans
-- Enable Query Store:
-- ALTER DATABASE mydb SET QUERY_STORE = ON;
-- Find top resource-consuming queries:
-- SELECT TOP 20
-- qs.query_id,
-- qt.query_sql_text,
-- rs.avg_duration / 1000.0 AS avg_ms,
-- rs.count_executions,
-- rs.avg_logical_io_reads,
-- rs.avg_cpu_time / 1000.0 AS avg_cpu_ms
-- FROM sys.query_store_runtime_stats rs
-- JOIN sys.query_store_plan qp ON rs.plan_id = qp.plan_id
-- JOIN sys.query_store_query q
...๐ Systematic Profiling Workflow
โ ๏ธ Common Mistake
Optimizing the slowest single query instead of the highest total-time query. A query running 50ms ร 100,000 times/day (5,000 seconds total) matters more than one running 10 seconds ร 2 times/day (20 seconds total).
Profiling Workflow
7-step systematic performance tuning
-- PROFILING WORKFLOW: systematic performance tuning
-- Step 1: IDENTIFY slow queries
-- Use pg_stat_statements, slow query log, or APM tools
-- Focus on: highest total time (not just slowest single execution)
-- Step 2: ANALYZE the execution plan
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders
WHERE customer_id = 42 AND status = 'pending';
-- Look for: Seq Scan, high actual rows, buffer reads
-- Step 3: CHECK statistics freshness
SELECT relname, last_analyze, last_autoanalyze,
n_live_
...๐ Quick Reference
| Database | Profiling Tool |
|---|---|
| PostgreSQL | EXPLAIN ANALYZE + pg_stat_statements |
| MySQL | Performance Schema + slow query log |
| SQL Server | Query Store + DMVs |
| All | APM tools (Datadog, New Relic) |
๐ Lesson Complete!
You can now profile queries across PostgreSQL, MySQL, and SQL Server. Next, dive into buffer pool and memory optimization!
Sign up for free to track which lessons you've completed and get learning reminders.