RDS/Aurora performance without defaulting to “scale the instance.”
Use workload evidence to separate expensive statements, plan problems, connection amplification, waits and configuration from genuine capacity constraints.
Use workload evidence to separate expensive statements, plan problems, connection amplification, waits and configuration from genuine capacity constraints.
Which statements, calls and I/O patterns actually account for database time?
Distinguish a real resource ceiling from a workload that would simply consume the larger instance too.
Combine managed-service timelines with application and performance validation rather than treating upgrades as a checkbox.
Most RDS and Aurora teams already have the evidence. It is spread across consoles and rarely lined up on the same timeline.
| Source | What it answers |
|---|---|
| CloudWatch Database Insights (formerly Performance Insights) | Database load by wait event and by SQL statement over time. Shows whether the instance is busy on CPU, I/O, locks or client waits, and which statements drive it. Advanced mode adds execution plans and lock analysis. |
pg_stat_statements | Total time, calls, rows and buffer use per normalized statement. Ranks the workload by total cost instead of by the slowest single query. |
| CloudWatch instance metrics | CPU, freeable memory, read/write IOPS and latency, disk queue depth, connections and replica lag. On burstable classes and gp2 storage, CPU credit and burst balance show whether the ceiling is real or a credit drain. |
| Enhanced Monitoring | OS-level view at up to one-second granularity: per-process CPU and memory, load and swap. This separates PostgreSQL work from the operating system and AWS agents. |
Parameter group and pg_settings | Non-default settings and what changed. For example work_mem, autovacuum thresholds, random_page_cost, max_connections and logging settings. |
| PostgreSQL logs (exported to CloudWatch Logs) | Slow statements via log_min_duration_statement, plans via auto_explain, lock waits via log_lock_waits, checkpoints and autovacuum activity. |
| RDS Proxy metrics, if used | Client and database connection counts, and sessions pinned to a backend connection. Pinning can quietly cancel the pooling you are paying for. |
| RDS events | Failovers, maintenance, minor-version upgrades, parameter changes and storage scaling. Use them to line up a regression with what changed. |
All of this is read-only. Your team exports it; no credentials or production write access are needed.
CPU climbs and the instance class goes up a size. Database Insights still shows the same three statements dominating load, so the bigger instance fills again as data grows.
Connection counts spike after a deploy or a retry storm. Each query is fast; there are just too many backends competing. Pool sizing, RDS Proxy pinning and idle-in-transaction sessions matter more than indexes.
Latency rises with disk queue depth while CPU looks fine. Check provisioned IOPS and throughput, burst balance, and whether the working set still fits in memory.
On Aurora, read and write I/O is part of the bill on the Standard storage configuration. Scan-heavy workloads can show up in cost before they show up in latency.
Start with the problem and the evidence you already have. You will get a direct answer on whether a scoped investigation is the right next step.
Prefer to talk first? Book a 20-minute triage call.