Quand le planificateur choisit des plans parallèles
Comprenez les seuils de coût et les tailles de table qui déclenchent les parcours et les jointures parallèles.
Quand le planificateur choisit des plans parallèles est une leçon PostgreSQL Performance & Query Optimization gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage PostgreSQL Performance & Query Optimization, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours PostgreSQL Performance & Query Optimization comprend 4 leçons au total.
Certaines parties de cette leçon n'ont pas encore été traduites et s'affichent en anglais.
Parallelism Is a Cost Decision
PostgreSQL does not run every query in parallel. The planner only generates a parallel plan when it believes the extra cost of spinning up workers will be repaid by faster execution.
- Workers cost money: the planner adds startup and tuple-transfer overhead for each one.
- Parallelism only pays off when the table is large enough and the work is CPU- or IO-heavy enough.
- Tiny tables and cheap queries stay sequential — the overhead would dominate.
In this lesson you will learn exactly which thresholds and table sizes flip the planner into a parallel plan.
The Three Gating GUCs
Three settings gate whether parallelism is even considered before any cost math runs:
- max_parallel_workers_per_gather — max workers a single Gather node may request (default 2). If 0, parallelism is disabled for that query.
- max_parallel_workers — cap across the whole instance (default 8).
- max_worker_processes — the hard OS-level ceiling on background workers (default 8).
If max_parallel_workers_per_gather = 0, the planner never produces a parallel path, regardless of table size.
SHOW max_parallel_workers_per_gather;
SHOW max_parallel_workers;
SHOW max_worker_processes;The Size Threshold: min_parallel_table_scan_size
The single most important table-size trigger is min_parallel_table_scan_size (default 8MB). A relation must be at least this large before the planner even considers a parallel sequential scan over it.
- For indexes, the analogous setting is min_parallel_index_scan_size (default
512kB). - Below the threshold, the relation is treated as too small to bother parallelizing.
So a 6MB table will almost never get a parallel scan, while a 1GB table easily clears the bar.
SHOW min_parallel_table_scan_size;
SHOW min_parallel_index_scan_size;How Worker Count Scales With Size
The number of workers is not fixed — it grows logarithmically with table size. Each time the relation triples past the threshold, the planner allows one more worker (capped by max_parallel_workers_per_gather).
- 8MB to 24MB: up to 1 worker
- 24MB to 72MB: up to 2 workers
- 72MB to 216MB: up to 3 workers
- Each step multiplies the previous boundary by 3.
This is why doubling a small table rarely changes the worker count, but going from 10MB to 1GB does.
The Per-Tuple and Setup Costs
Once a parallel path is possible, the planner weighs two cost penalties against the sequential plan:
- parallel_setup_cost (default
1000) — a one-time charge for launching workers and tearing down the shared memory. This is large, which is why tiny queries stay sequential. - parallel_tuple_cost (default
0.1) — charged per tuple passed from a worker up through the Gather node to the leader.
A parallel plan wins only when divided per-worker scan cost minus these penalties beats the sequential cost.
SHOW parallel_setup_cost;
SHOW parallel_tuple_cost;Reading a Parallel Plan in EXPLAIN
You confirm a parallel plan by looking for a Gather (or Gather Merge) node and Parallel-prefixed scan nodes underneath it.
Gathercollects rows from workers;Workers Plannedshows how many were requested.- Nodes below the Gather (e.g.
Parallel Seq Scan) run once per worker plus the leader. - Row counts under the Gather are per-worker estimates, not the total.
EXPLAIN (ANALYZE, VERBOSE)
SELECT count(*)
FROM orders
WHERE amount > 100;Forcing Parallelism for Testing
To observe a parallel plan on a smaller table during testing, lower the gating thresholds at the session level. This does not change the data — only the planner's willingness to parallelize.
- Set
min_parallel_table_scan_size = 0to remove the size gate. - Set
parallel_setup_cost = 0andparallel_tuple_cost = 0to remove the cost penalties. - Set
force_parallel_mode = on(renameddebug_parallel_queryin PG16+) to force a parallel path where one exists.
These are diagnostic knobs — never leave them on in production.
SET min_parallel_table_scan_size = 0;
SET parallel_setup_cost = 0;
SET parallel_tuple_cost = 0;
SET max_parallel_workers_per_gather = 4;Parallel-Aware vs Parallel-Restricted
Not every operation can run inside a parallel worker. The planner classifies pieces of the query:
- Parallel-safe — can run in a worker (most immutable/stable functions, plain scans).
- Parallel-restricted — must run only in the leader (e.g. references to temporary tables, correlated subplans, some CTEs).
- Parallel-unsafe — disables parallelism for the whole query (e.g. functions marked
PARALLEL UNSAFE, writing CTEs, sequence calls).
A single VOLATILE or PARALLEL UNSAFE function in the query can suppress an otherwise-parallel plan.
CREATE FUNCTION score(x int) RETURNS int
LANGUAGE sql IMMUTABLE PARALLEL SAFE
AS 'SELECT x * 2';Parallel Joins and Aggregates
Parallelism is not limited to scans. Once a driving relation is scanned in parallel, the planner can push joins and aggregation into the workers:
- Parallel Hash Join — workers cooperatively build one shared hash table.
- Parallel Nested Loop / Merge Join — each worker joins its slice of the outer relation.
- Partial Aggregate — each worker aggregates its rows, then a
Finalize Aggregateabove the Gather combines the partial results.
This is why a large GROUP BY over a big fact table is a classic parallel-plan candidate.
EXPLAIN
SELECT customer_id, count(*)
FROM orders
GROUP BY customer_id;Things That Quietly Disable Parallelism
Even on a huge table, several conditions block a parallel plan entirely:
- The query is run inside a cursor/
FETCHor withSELECT ... FOR UPDATErow locking. - It is a data-modifying statement on PG versions before 11 (later versions allow parallel
INSERT ... SELECT/ index builds, but not parallel UPDATE/DELETE execution). - It calls a
PARALLEL UNSAFEfunction or runs inside a function that has already claimed all workers. max_parallel_workers_per_gather = 0for the session.
When a plan you expect to be parallel is not, check these before tuning costs.
Putting the Thresholds Together
For the planner to pick a parallel plan, all of these must hold:
- Relation size at least
min_parallel_table_scan_size(8MB) for scans, or the index size threshold. max_parallel_workers_per_gather > 0and instance-wide worker budget available.- The query contains only parallel-safe (or at worst parallel-restricted) elements.
- The estimated parallel cost, after
parallel_setup_costandparallel_tuple_cost, beats the best sequential plan.
Miss any one and you get a sequential plan — usually for a good reason.
EXPLAIN (ANALYZE, BUFFERS)
SELECT sum(amount)
FROM orders
WHERE created_at >= DATE '2024-01-01';Quick Check
Test your understanding of the parallel-plan triggers.
Recap
Key takeaways on when PostgreSQL chooses a parallel plan:
- Parallelism is a cost decision gated first by size, then by cost math.
- min_parallel_table_scan_size (8MB) is the primary table-size trigger; min_parallel_index_scan_size (512kB) covers indexes.
- Worker count grows logarithmically — one more worker each time size triples — capped by
max_parallel_workers_per_gather. - parallel_setup_cost (1000) and parallel_tuple_cost (0.1) must be outweighed by the per-worker speedup.
- Parallel-unsafe functions, row locking, and a zero worker setting can disable parallelism even on huge tables.
- Confirm plans by looking for Gather / Partial Aggregate nodes in EXPLAIN.
Questions Fréquemment Posées
La leçon « Quand le planificateur choisit des plans parallèles » est-elle gratuite ?
Oui — le texte complet de « Quand le planificateur choisit des plans parallèles » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours PostgreSQL Performance & Query Optimization, passe à CoddyKit PRO. Le cours PostgreSQL Performance & Query Optimization comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Quand le planificateur choisit des plans parallèles » ?
Comprenez les seuils de coût et les tailles de table qui déclenchent les parcours et les jointures parallèles. Tu pratiques PostgreSQL Performance & Query Optimization avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer PostgreSQL Performance & Query Optimization ?
Aucune expérience préalable n'est requise. PostgreSQL Performance & Query Optimization sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.
Combien de temps prend la leçon « Quand le planificateur choisit des plans parallèles » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon PostgreSQL Performance & Query Optimization ?
Oui. Chaque leçon PostgreSQL Performance & Query Optimization inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Quand le planificateur choisit des plans parallèles
- Régler le nombre de processus et le coût de rassemblement
- Agrégation parallèle et jointures par hachage
- Diagnostiquer la désactivation du parallélisme