PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 10.23 » Langage SQL » Requêtes parallélisées » Plans parallélisés

15.3. Plans parallélisés

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.

15.3.1. Parcours parallélisés

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.

15.3.2. Jointures parallélisées

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.

15.3.3. Agrégations parallélisées

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.

15.3.4. Conseils pour les plans parallélisés

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.