CONCEPT Cited by 1 source
work_mem multiplication¶
Definition¶
work_mem multiplication is the geometric amplification
that turns a modest per-allocation cap (e.g. 4 MB) into
potentially gigabytes of transient RSS
under load. Postgres's work_mem is not a per-query or
per-connection cap — it is a per-sort-node, per-hash-node,
per-query, per-backend cap, and the effective multiplier is:
A 4 MB work_mem with 100 active connections running queries
with 3 sort/hash nodes each ⇒ up to 1.2 GB of transient
RSS — a factor of 300× the nominal setting.
(Source: sources/2026-04-21-planetscale-high-memory-usage-in-postgres-is-good-actually.)
Simeon Griggs's framing¶
"Tuning
work_memmight seem like an obvious lever — decrease it to reduce RSS, or increase it to prevent operations from spilling to disk. But the allocation is per-sort/hash-node, per-query, per-backend. A single complex query can allocatework_memmultiple times, and that multiplies across every active connection. Setting it too low forces more disk I/O; setting it too high globally can cause total memory usage to spike unpredictably under load. Neither direction is a safe default change without first understanding your workload's concurrency and query complexity."
Why this is structurally unsafe¶
The two failure modes of work_mem tuning are symmetric:
- Set too low: Queries that would fit in memory at the default get pushed to on-disk sort / hash-join, multiplying I/O cost and latency. Operator sees elevated disk I/O and slow queries; CPU and memory look fine.
- Set too high: Under peak concurrency, the multiplier unpredictably saturates RAM. Operator sees OOM-kill events. Average utilisation looked healthy because the multiplication only manifests under correlated-workload bursts.
The operator does not generally know the multiplier at configuration time because it depends on runtime concurrency + query shape.
Contrast with MySQL-style per-session caps¶
MySQL's sort_buffer_size is per-session, not per-node
within a query. A single MySQL session with 10 sort operations
allocates sort_buffer_size × 1 (reused), not × 10. Postgres's
per-node semantics makes the multiplier larger for comparable
queries and makes configuration correspondingly harder.
Operational discipline¶
Griggs's implicit prescription:
- Don't tune
work_memas a default RSS-reduction lever. Connection pooling (reducingactive_backends) is safer. - Characterise workload concurrency and query complexity first. If you don't know the multiplier, you can't set the limit.
- Consider per-role
SET work_memfor known-heavy workloads (analytics, ETL) rather than global tuning.
Seen in¶
- sources/2026-04-21-planetscale-high-memory-usage-in-postgres-is-good-actually — canonical wiki disclosure of the per-sort-node × per-query × per-backend multiplication geometry and the "not a safe default change" discipline.