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. Habituellement, cela signifie que le parcours de la table pour cette requête sera un parcours parallélisé sûr.
Les types suivants de parcours parallèles de table sont actuellement supportés.
Lors d'un parcours séquentiel parallèle, les blocs de la table seront divisés entre les processus participant au parcours. Les blocs sont retournés un à la fois, afin que l'accès à la table reste séquentiel.
Lors d'un parcours de bitmap parallèle, un processus est choisi pour être le dirigeant. Ce processus effectue le parcours d'un ou plusieurs index et créer un bitmap indiquant les blocs de la table devant être visités. Ces blocs sont alors divisés entre les processus participant au parcours comme lors d'un parcours d'accès séquentiel. En d'autres termes, le parcours de la table est effectué en parallèle, mais le parcours d'index associé ne l'est pas.
Lors d'un parcours d'index parallèle ou d'un parcours d'index seul parallèle, les processus participant au parcours se relaient pour lire les données depuis l'index. Actuellement, les parcours d'index parallèles ne sont supportés que pour les index btree. Chaque processus réclamera un seul bloc de l'index et scannera et retournera toutes les lignes référencées par ce bloc; les autres processus peuvent être en train de retourner des lignes d'un bloc différent de l'index au même moment. Les résultats d'un parcours d'index parallèle sont retournés triés au sein de chaque worker parallèle.
Les autres types de parcours, comme les parcours d'index autre que btree, pourraient supportés la parallélisation dans le futur.
Tout comme les plans non parallèles, la table conductrice peut être jointe à une ou plusieurs autres tables en utilisant une boucle imbriquée, une jointure par hashage ou une jointure par tri-fusion. Le côté interne de la jointure peut être n'importe quel type de plan non parallèle qui est par ailleurs supportés par l'optimiseur pourvu qu'il soit sûr de le lancer au sein d'un worker parallèle. Par exemple, si une boucle imbriquée est choisie, le côté interne pourrait être un parcours d'index dont la valeur de recherche est prise depuis le côté externe de la jointure.
Chaque worker exécutera la partie interne de la jointure en entier. Ce n'est généralement pas un problème pour les boucles imbriquées, mais cela peut se révéler inefficace pour les cas impliquant des jointures par hashage ou tri-fusion. Par exemple, pour une jointure par hashage, cette restriction signifie qu'une table de hashage identique est créé dans chaque worker, ce qui fonctionne bien pour les jointures de petites tables mais qui pourrait ne pas être efficace quand la table interne est grande. Pour une jointure par tri-fusion, cela pourrait signifier que chaque worker réalise un tri séparé de la relation interne, ce qui pourrait être lent. Bien entendu, dans les cas où un plan parallèle de ce type ne serait pas efficace, l'optimiseur de requête choisira normalement un autre type de plan (peut-être un qui n'utilise pas de parallélisme) à la place.
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
ou Gather Merge
. 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 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 et Section 15.4 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.