Dr. Ibrar Ahmed

HomePostgreSQLArticle

PostgreSQL Mechanics

PostgreSQL Uses 1 of 16 CPU Cores. Here's the Fix.

Dr. Ibrar Ahmed3 min readFrom the lecture notes

What you will learn

  • I have seen this pattern many times in production PostgreSQL
  • Eighteen point four seconds, down to four point one.
  • shared_buffers was a hundred and twenty-eight megabytes.
  • (SLOW DOWN: 45-60s, this is the payoff, let it breathe)
  • PostgreSQL's shared_buffers, plus the operating system's page
01

The production symptom

I have seen this pattern many times in production PostgreSQL

systems. The team was already thinking about more hardware. /

One query took eighteen seconds, on a server with a hundred

02

Hero Proof

Eighteen point four seconds, down to four point one.

That's more than four times faster, and it cost nothing in

hardware. Let me show you exactly how we got there.

Verification 4
sql
SHOW effective_cache_size;
03

The Diagnosis

shared_buffers was a hundred and twenty-eight megabytes. On a

hundred and twenty-eight gig server, that's about a tenth of

effective_cache_size was four gigs, so the planner thought

Verification 6
sql
ALTER SYSTEM SET effective_cache_size = '96GB'; -- reload
04

The Query

Here's the query. Nothing exotic. Top fifty customers by orders in the last ninety days. A join. A date filter. A group by. An order by. Normal reporting SQL. But it takes over eighteen thousand milliseconds, and it reads almost forty-six thousand pages from disk. So before you rewrite the query, stop.

The SQL is not the first suspect. The plan is.

Verification 2
sql
postgres-# SELECT c.id, c.name, count(*) FROM customers c
Verification 5
sql
EXPLAIN SELECT * FROM orders WHERE customer_id = 42;
05

Explain Analyze

And the plan tells you everything. This is EXPLAIN ANALYZE,

Look at the sort: "external merge", five hundred and forty-two

megabytes written to disk. That's work_mem being too small.

Inspect the system
sql
EXPLAIN (ANALYZE, BUFFERS)
06

The Five Steps

So here's the plan, in order. Five steps. Measure first, with EXPLAIN ANALYZE, always. Then fix the planner's view of memory. Then give PostgreSQL real cache. Then deal with the sorts. And finally, parallel workers. One change at a time. Measure after each one. Never touch two settings at once, or you'll never know which one actually helped.

07

effective cache size

It does not allocate memory. It's just the planner's estimate

Four gigs to ninety-six. About seventy-five percent of RAM.

Now the planner has a better chance to pick the right scan.

08

shared buffers

Setting two: shared_buffers. This is PostgreSQL's own cache,

the pages it keeps in memory so it doesn't go to disk.

The default was a hundred and twenty-eight megs. We move it to

Verification 3
sql
SELECT name, setting, unit FROM pg_settings WHERE name IN
09

work mem

Setting three: work_mem. This is the one that bites if you get

with parallel workers and a couple of sorts can use it several

So do not set it to a gigabyte globally. Fifty sessions, two

10

parallel workers

Setting four: parallel workers. You're raising the ceiling,

you're not forcing anything. The planner still decides whether

On a sixteen-core box, a default of two per gather is low. Four

11

Before & After

(SLOW DOWN: 45-60s, this is the payoff, let it breathe)

Eighteen thousand two hundred thirty-four milliseconds.

Forty-five thousand eight hundred ninety-two disk reads.

12

Memory Map

PostgreSQL's shared_buffers, plus the operating system's page

work_mem is separate. It's per-operation scratch space, for

And disk is the fallback. It's the slowest thing in this

13

TUNING LADDER, recap

Measure first. Tell the planner the truth about your cache.

Give PostgreSQL enough memory to cache with. Fix sort spills

carefully, never globally. Raise the parallel ceiling.

14

Protect The Gain

(fast, ~20s) You fixed the server. Now protect the gain. Watch three things: your cache hit ratio, your heaviest queries in pg_stat_statements, and EXPLAIN with buffers. Tuning is not a one-time event. Measure again after the workload changes.

15

Next Steps

(fast, ~20s) The loop, for any slow query: Baseline. One setting. Reload or restart. Re-test. And if it got worse, roll it back. Your servers have capacity. Your defaults don't.