RDS CPU at 100%
Performance Insights, slow query log, EXPLAIN, missing indexes, autovacuum, and connection storms.
Overview
Overview
RDS CPU at 100% slows every query proportionally and can eventually make the instance unresponsive. The root cause is almost always one of four things: an expensive query, a traffic spike, lock contention or autovacuum on bloated PostgreSQL tables.
Likely causes
- Expensive query — Missing index or full table scan: every row is read and processed
- Traffic spike — A sudden increase in query volume
- Lock contention — Blocked queries queue behind the lock holder
- Autovacuum — Full scans on bloated PostgreSQL tables
- Connection storm — Hundreds of connections retry at once and setup overhead saturates CPU
Tools and what they tell you
- Performance Insights (db.t3.medium and larger) — Database load broken down by wait event: CPU, locks, I/O, network
- Top SQL tab — Which queries consume the most CPU time; the fastest way to find the offender
- EXPLAIN ANALYZE — Sequential scan (bad) or index scan (good), estimated vs actual rows, time per node
- pg_stat_user_tables — Dead tuples and last autovacuum per table
Monitor dead tuples with: SELECT schemaname, relname, n_dead_tup, last_autovacuum FROM pg_stat_user_tables ORDER BY n_dead_tup DESC.
Failure mechanisms
> [!TIP] A sequential scan on a large table is the classic CPU spike; the fix is usually an index on the filtered or joined column.
> [!WARNING] Exponential backoff with jitter is supposed to prevent connection storms, but it is often not implemented.
Key concepts
- Performance Insights
- RDS feature showing database load by wait event and top SQL. Identifies which queries use the most CPU, which are waiting on locks or I/O. Available for most instance classes.
- EXPLAIN ANALYZE
- Shows the actual execution plan with timing. Look for Seq Scan on large tables (should be Index Scan), high actual rows vs estimated rows (stats out of date), and total cost.
- pg_stat_statements
- PostgreSQL extension that aggregates query statistics: total time, calls, mean time. Enable with shared_preload_libraries = pg_stat_statements. Survives until reset.
- Autovacuum
- PostgreSQL background process reclaiming dead tuples from UPDATE/DELETE. Bloated tables trigger aggressive autovacuum that competes with production queries for CPU.
- RDS Proxy
- Connection multiplexer that pools connections to RDS. Reduces connection establishment overhead and protects against connection storms. Useful for Lambda-heavy architectures.
When to use it
- Diagnosing a sudden RDS CPU spike during a traffic increase
- Finding the slow query responsible for a production performance incident
- Explaining why autovacuum causes periodic CPU spikes in PostgreSQL
- Designing a database indexing strategy to support a new query pattern
Interview questions
- Your RDS CPU is at 100%. Walk through how you identify and fix the root cause.
Sign up free to read the full topic
More Troubleshooting topics
- NAT Gateway Bandwidth and Throttling
- Network Bandwidth Saturation
- OOMKilled Pod Diagnosis
- Packet Loss and Network Degradation
- Pods Stuck Pending
- PVC Mount Failures in Kubernetes
- RDS Read Replica Replication Lag
- S3 High Latency Spikes
- Slow Query Diagnosis
- Staging Passes, Prod Fails
- TLS Certificate Expiry Incidents
- VPC Connectivity Failures
Start free on Cariara · Coding problems · System design studio