PostgreSQL™ peut préparer des plans de requêtes utilisant plusieurs CPU pour répondre plus rapidement à certaines requêtes. Cette fonctionnalité est connue sous le nom de requêtes parallélisées. Un grand nombre de requêtes ne peuvent pas bénéficier de cette fonctionnalité, soit à cause de la limitation de l'implémentation actuelle soit parce qu'il n'existe pas de plan imaginable qui soit plus rapide qu'un plan sériel. Néanmoins, pour les requêtes pouvant en bénéficier, l'accélération due à une requête parallélisée est souvent très significative. Beaucoup de ces requêtes peuvent s'exécuter au moins deux fois plus rapidement grâce à la parallélisation, et certaines requêtes quatre fois voire plus. Les requêtes touchant à une grande quantité de données mais ne retournant que quelques lignes à l'utilisateur sont généralement celles qui bénéficient le plus de cette fonctionnalité. Ce chapitre explique quelques détails sur le fonctionnement des requêtes parallélisées et dans quelles situations elles peuvent être utilisées pour que les utilisateurs intéressés sachent quoi en attendre.
Quand l'optimiseur détermine que la parallélisation est la stratégie la plus rapide pour une requête particulière, il crée un plan d'exécution incluant un nœud Gather. En voici un exemple simple :
EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%'; QUERY PLAN ------------------------------------------------------------------------------------- Gather (cost=1000.00..217018.43 rows=1 width=97) Workers Planned: 2 -> Parallel Seq Scan on pgbench_accounts (cost=0.00..216018.33 rows=1 width=97) Filter: (filler ~~ '%x%'::text) (4 rows)
Dans tous les cas, le nœud Gather aura extactement un noœud enfant, qui est la portion du plan exécutée en parallèle. Si le nœud Gather est à la racine du plan, alors la requête entière est parallélisée. S'il est placé ailleurs dans le plan, alors seulement cette portion du plan s'exécutera en parallèle. Dans l'exemple ci-dessus, la requête accède à une seule table, donc il n'existe qu'un seul autre nœud de plan que le nœud Gather lui-même ; comme ce nœud est un enfant du nœud Gather, il s'exécutera en parallèle.
En utilisant EXPLAIN, vous pouvez voir le nombre de processus d'aide (appelés workers) choisis par le planificateur. Quand le nœud Gather est atteint lors de l'exécution de la requête, le processus en charge de la session demandera un nombre de processus d'arrière plan (background workers) égal au nombre de workers choisi par le planificateur. Le nombre total de background workers pouvant exister à un même moment est limité par le paramètre max_worker_processes ; il est donc possible qu'une requête parallélisée s'exécute avec moins de workers que prévu, voire même sans worker du tout. Le plan optimal peut dépendre du nombre de workers disponibles, ce qui peut résulter en de médiocres performances des requêtes. Si cela survient fréquemment, étudiez l'augmentation de max_worker_processes pour qu'un plus grand nombre de workers puissent travailler simultanément ou la diminution de max_parallel_workers_per_gather pour que le planificateur réclame moins de workers.
Chaque processus background worker démarré avec succès dans une requête parallélisée donnée exécutera la portion du plan descendant du nœud Gather. Le processus principal, appelé leader, exécutera aussi cette portion du plan bien qu'il ait des responsabilités supplémentaires : il doit aussi lire toutes les lignes générées par les workers. Quand la portion parallélisée du plan ne génère qu'un petit nombre de lignes, le leader se comportera souvent comme un worker supplémentaire, accélérant l'exécution de la requête. Par contre, quand la portion parallèle du plan génère un grand nombre de lignes, le leader peut être accaparé par la lecture des lignes générées par les workers et par le traitement des autres étapes au-dessus du nœud Gather. Dans de tels cas, le leader travaillera très peu sur la portion parallélisée du plan.