How to fix high CPU on a PostgreSQL database
Quick answer: High CPU is most often the queries, not the config, start there. See what is running now in
pg_stat_activity(rows wherestate = 'active'andbackend_type = 'client backend'), rank cumulative cost inpg_stat_statementsbytotal_exec_time, and fix the cause , usually a missing index forcing sequential scans, a plan that regressed after stats drift, or too many active connections. Reaching for a config knob first is the common mistake.
Step 1: What is running right now?
pg_stat_activity is the live view, one row per connection:
SELECT pid, state, wait_event_type, wait_event,
now() - query_start AS running_for,
query
FROM pg_stat_activity
WHERE state = 'active'
AND backend_type = 'client backend'
ORDER BY running_for DESC;
Filter on backend_type = 'client backend' or the list fills with autovacuum
workers, parallel workers, and replication processes, which are also active,
but are not the application queries you are hunting.
- Many
activerows running the same query shape → a hot query hammering the CPU (often an un-indexed one, run constantly). - A few very long-running
activequeries → an expensive scan, sort, or aggregate. wait_event_typetells you compute vs waiting: no wait event = running on CPU;IOorLock= blocked, not compute-bound.
Step 2: What costs the most over time?
Live activity shows the moment; pg_stat_statements shows the pattern:
SELECT calls, total_exec_time, mean_exec_time, query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;
The top of this list is where your CPU goes. See
identifying slow queries
for how to read it, remember total_exec_time = calls × mean_exec_time, so a
cheap query run constantly can dominate.
Step 3: Match the symptom to the cause
| What you see | Likely cause | Fix |
|---|---|---|
Top query does a Seq Scan on a big table | Missing index | Add the index (find missing indexes) |
| A query that used to be fast is now hot | Plan regression from stats drift | ANALYZE; see why it got slow |
| Hundreds of active backends | Too many connections | Add a connection pooler |
| Slow sorts/hashes spilling to disk | Under-sized work_mem or SELECT * moving too much | Select fewer columns; tune work_mem carefully, note a spill is mostly I/O, with some added CPU from extra merge passes |
Notice how few of these are "change a setting." The lever is usually the query or an index, though not always. Configuration and contention can drive CPU on their own: far more connections than cores (context-switch thrash), no connection pooler in front of a serverless app, or JIT compilation firing on short queries where it costs more than it saves. Rule the queries out first, then look there.
Why does aI-generated code cause CPU spikes?
Your AI coding agent (Claude Code, Cursor) writes queries that are correct but
blind to cost: an un-indexed filter that becomes a Seq Scan, an N+1 loop that
fires thousands of small queries, a SELECT * that hashes far more data than
needed. None of that looks expensive in the source. CPU load is an emergent
property of those queries meeting production data volume, which the model never
sees.
How do I keep CPU under control?
- Watch
pg_stat_activityfor a pile-up of the same active query. - Keep
pg_stat_statementssorted bytotal_exec_timein a dashboard. - Index the scans, fix the regressions, and pool the connections before touching server config.
How DBGorilla helps
DBGorilla connects read-only and gives your AI coding agent (Claude Code, Cursor)
the live picture, the active queries in pg_stat_activity, the top
total_exec_time statements, the plans behind them, so it can point at the
query burning CPU and explain the fix (an index, a rewrite, pooling) instead of
guessing at config. It surfaces and explains; it does not touch production.
Get started free →
FAQ
What causes high CPU usage in PostgreSQL? Most often the queries: missing indexes forcing sequential scans, a plan regression, expensive sorts/aggregates, or too many active connections. Config and contention (connection storms, JIT on short queries) can also be the primary cause, start with the queries, then look there.
How do I find which query is burning CPU right now?
pg_stat_activity where state = 'active', ordered by now() - query_start,
shows live work; pg_stat_statements by total_exec_time shows cumulative cost.
Is high CPU I/O or compute?
Check wait_event_type: no wait event means running on CPU; IO or Lock means
blocked. High CPU with little waiting is raw query work.
Will raising max_connections or shared_buffers fix it? Usually not, the queries are the cause. Index, fix the plan, or pool; raising limits often just runs more expensive work at once.