Documentation PostgreSQL 9.6.24 > Langage SQL > Requêtes parallélisées > Plans parallélisés | |
Quand la parallélisation des requêtes peut-elle être utilisée ? | Sécurité sur la parallélisation |
Comme chaque worker exécute la portion parallélisée du plan jusqu'à la fin, il n'est pas possible de prendre un plan de requête ordinaire et de l'exécuter en utilisant plusieurs workers. Chaque worker produirait une copie complète du jeu de résultats, donc la requête ne s'exécuterait pas plus rapidement qu'à la normale, et produirait des résultats incorrects. À la place, la portion parallélisée du plan est considéré en interne par l'optimiseur comme un plan partiel ; c'est-à-dire construit de façon à ce que chaque processus exécutant le plan ne génère qu'un sous-ensemble des lignes en sortie, et que chacune ait la garantie d'être générée par exactement un des processus participants.
Actuellement, le seul type de parcours qui ait été modifié pour fonctionner avec des requêtes parallélisées est le parcours séquentiel (Seq Scan). De ce fait, la table en question dans un plan parallélisé sera toujours parcourue en utilisant un Parallel Seq Scan. Les blocs de la relation sont répartis entre les processus participants. Les blocs sont gérés un par un, donc cet accès à la relation reste séquentiel. Chaque processus visite chaque ligne du bloc qui lui est assigné avant de réclamer un nouveau bloc.
La table peut être jointe à une ou plusieurs autres tables en utilisant une boucle imbriquée (nested loop ou une jointure par hachage (hash join ). Le côté interne de la jointure peut être n'importe quel type de plan non parallélisé supporté par le planificateur par ailleurs, pourvu qu'il soit sûr de l'exécuter dans un worker parallélisé. Par exemple, ce peut être un parcours d'index recherchant une valeur prise dans la table externe. Chaque worker exécutera complètement le côté interne de la jointure, ce qui explique que les workers d'une jointure par hachage construisent chacun une table de hachage identique.
PostgreSQL™ procède à l'agrégation parallélisée en deux étapes. Tout d'abord, chaque processus de la partie parallélisée de la requête réalise une étape d'agrégation, produisant un résultat partiel pour chaque groupe qu'il connaît. Ceci se reflète dans le plan par le nœud PartialAggregate. Puis les résultats partiels sont transférés au leader via le nœud Gather. Enfin, le leader ré-agrège les résultats partiels de tous les workers pour produire le résultat final. Ceci apparaît dans le plan sous la forme d'un nœud Finalize Aggregate.
Comme le nœud Finalize Aggregate s'exécute sur le processus leader, les requêtes produisant un nombre relativement important de groupes en comparaison du nombre de lignes en entrée apparaîtront moins favorable au planificateur de requêtes. Par exemple, dans le pire scénario, le nombre de groupes vus par le nœud Finalize Aggregate pourrait être aussi grand que le nombre de lignes en entrée qui ont été traitées par les processus worker à l'étape Partial Aggregate. Dans de tels cas, il n'y aura clairement aucun intérêt au niveau des performances à utiliser l'agrégation parallélisée. Le planificateur de requêtes prend cela en compte lors du processus de planification et a peu de chance de choisir un agrégat parallélisé sur ce scénario.
L'agrégation parallélisée n'est pas supportée dans toutes les situations. Chaque agrégat doit être sûr à la parallélisation et doit avoir une fonction de combinaison. Si l'agrégat a un état de transition de type internal, il doit avoir des fonctions de sérialisation et de désérialisation. Voir CREATE AGGREGATE(7) pour plus de détails. L'agrégation parallélisée n'est pas supportée si un appel à la fonction d'agrégat contient une clause DISTINCT ou ORDER BY ainsi que pour les agrégats d'ensembles triés ou quand la requête contient une clause GROUPING SETS. Elle ne peut être utilisée que si toutes les jointures impliquées dans la requête sont dans la partie parallélisée du plan.
Si une requête ne produit pas un plan parallélisé comme attendu, vous pouvez tenter de réduire parallel_setup_cost ou parallel_tuple_cost. Bien sûr, ce plan pourrait bien finir par être plus lent que le plan sériel préféré par le planificateur mais ce ne sera pas toujours le cas. Si vous n'obtenez pas un plan parallélisé même pour de très petites valeurs de ces paramètres (par exemple après les avoir définis tous les deux à zéro), le planificateur a peut-être une bonne raison pour ne pas le faire pour votre requête. Voir Section 15.2, « Quand la parallélisation des requêtes peut-elle être utilisée ? » et Section 15.4, « Sécurité sur la parallélisation » pour des explications sur les causes possibles.
Lors de l'exécution d'un plan parallélisé, vous pouvez utiliser EXPLAIN (ANALYZE, VERBOSE) qui affichera des statistiques par worker pour chaque nœud du plan. Ce peut être utile pour déterminer si le travail est correctement distribué entre les nœuds du plan et plus généralement pour comprendre les caractéristiques de performance du plan.