PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Beaucoup de ceux qui utilisent dĂ©jĂ  explain.tensor.ru — notre service de visualisation des plans PostgreSQL ne sont peut-ĂȘtre pas au courant d'une de ses super capacitĂ©s : transformer un extrait de journal de serveur difficile Ă  lire


PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

 en une requĂȘte joliment formatĂ©e avec des suggestions contextuelles pour les nƓuds correspondants du plan :

PostgreSQL Query Profiler : comment associer un plan et une requĂȘte
Dans cette analyse de la deuxiÚme partie de sa présentation au PGConf.Russia 2020 je vais expliquer comment nous avons réussi à le faire.

Le transcription de la premiĂšre partie, consacrĂ©e aux problĂšmes courants de performance des requĂȘtes et Ă  leurs solutions, est disponible dans l'article « Recettes pour les requĂȘtes SQL en dĂ©tresse ».


Lire la vidéo

Commençons par la coloration — et nous allons colorer non pas le plan, car nous l'avons dĂ©jĂ  colorĂ©, il est dĂ©jĂ  beau et clair, mais la requĂȘte.

Il nous a semblĂ© que ce « rouleau » non formatĂ© extrait du journal paraissait trĂšs peu attrayant et donc — peu pratique.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Surtout lorsque les dĂ©veloppeurs collent le corps de la requĂȘte (c'est bien sĂ»r un anti-modĂšle, mais cela arrive) en une seule ligne. Horrible !

Dessiner cela d'une maniÚre plus esthétique.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Et si nous pouvons le dessiner joliment, c'est-Ă -dire analyser et reconstruire le corps de la requĂȘte, alors nous pourrons Ă©galement « attacher » une suggestion Ă  chaque objet de cette requĂȘte — ce qui se passait Ă  chaque point correspondant du plan.

Arbre syntaxique de la requĂȘte

Pour ce faire, il faut d'abord analyser la requĂȘte.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Puisque notre noyau systĂšme fonctionne sur NodeJS, nous avons créé un module, vous pouvez le trouver sur GitHub. En fait, cela reprĂ©sente des « liaisons » avancĂ©es avec l'intĂ©rieur du parseur PostgreSQL lui-mĂȘme. C'est-Ă -dire que notre grammaire est simplement compilĂ©e de maniĂšre binaire, et des liaisons sont faites du cĂŽtĂ© NodeJS. Nous avons utilisĂ© des modules existants comme base — il n'y a pas de grand secret ici.

Nous alimentons le corps de la requĂȘte dans notre fonction — en sortie, nous obtenons un arbre syntaxique analysĂ© sous forme d'objet JSON.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Nous pouvons maintenant parcourir cet arbre Ă  l'envers et reconstruire la requĂȘte avec les indentations, couleurs, formatage que nous souhaitons. Non, ce n'est pas configurable, mais nous avons pensĂ© que c'Ă©tait ce qui serait le plus pratique.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Correspondance des nƓuds des requĂȘtes et des plans

Voyons maintenant comment nous pouvons combiner le plan que nous avons analysĂ© Ă  la premiĂšre Ă©tape et la requĂȘte que nous avons analysĂ©e Ă  la deuxiĂšme.

Prenons un exemple simple : nous avons une requĂȘte qui gĂ©nĂšre un CTE et le lit deux fois. Cela gĂ©nĂšre un tel plan.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

CTE

Si l'on y regarde de plus prÚs, jusqu'à la version 12 (ou en commençant par celle-ci avec le mot-clé MATERIALIZED) la formation du CTE est un obstacle indéniable pour le planificateur..
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Et donc, si nous voyons quelque part dans la requĂȘte une gĂ©nĂ©ration de CTE et quelque part dans le plan un nƓud CTE, ces nƓuds se « battent » entre eux, nous pouvons immĂ©diatement les combiner.

La tĂąche « avec une Ă©toile »: les CTE peuvent ĂȘtre imbriquĂ©s.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte
Ils peuvent ĂȘtre trĂšs mal imbriquĂ©s, et mĂȘme avoir le mĂȘme nom. Par exemple, vous pouvez Ă  l'intĂ©rieur de CTE A faire CTE X, et au mĂȘme niveau Ă  l'intĂ©rieur de CTE B refaire CTE X:

WITH A AS (
  WITH X AS (...)
  SELECT ...
)
, B AS (
  WITH X AS (...)
  SELECT ...
)
...

Lors de la correspondance, vous devez comprendre cela. Comprendre cela « avec les yeux » — mĂȘme en voyant le plan, mĂȘme en voyant le corps de la requĂȘte — c'est trĂšs difficile. Si votre gĂ©nĂ©ration de CTE est complexe, imbriquĂ©e, et que les requĂȘtes sont longues — alors c'est encore plus inconscient.

UNION

Si notre requĂȘte contient le mot-clĂ© UNION [ALL] (opĂ©rateur de jointure de deux sĂ©lections), alors dans le plan il correspond soit Ă  un nƓud Ajouter, soit Ă  quelque chose comme Union RĂ©cursive.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Ce qui est « en haut » de UNION est le premier enfant de notre nƓud, ce qui est « en bas » est le deuxiĂšme. Si nous avons « collĂ© » plusieurs blocs en mĂȘme temps Ă  travers UNION , le nƓud sera toujours un seul, mais aura beaucoup d'enfants — dans l'ordre oĂč ils viennent, respectivement : Ajouter(...) -- #1 UNION ALL (...) -- #2 UNION ALL (...) -- #3

  Append
  -> ... #1
  -> ... #2
  -> ... #3

: à l'intérieur de la génération d'une sélection récursive (

La tĂąche « avec une Ă©toile ») il peut Ă©galement y avoir plus d'unWITH RECURSIVE. Mais seul le dernier bloc aprĂšs le dernier est toujours rĂ©cursif. UNIONTout ce qui est au-dessus — c'est un, mais diffĂ©rent. UNIONWITH RECURSIVE T AS( (...) -- #1 UNION ALL (...) -- #2, ici se termine la gĂ©nĂ©ration de l'Ă©tat de dĂ©part de la rĂ©cursion. UNION ALL (...) -- #3, seul ce bloc est rĂ©cursif et peut contenir une rĂ©fĂ©rence Ă  T ) ... UNION:

De tels exemples doivent Ă©galement ĂȘtre « collĂ©s ». Dans cet exemple, nous voyons que

-les segments dans notre requĂȘte Ă©taient au nombre de 3. Par consĂ©quent, un nƓud correspond Ă  l'un, et l'autre Ă  UNIONLa lecture et l'Ă©criture des donnĂ©es. UNION correspond Ă  AjouterVoilĂ , nous avons tout dĂ©composĂ©, maintenant nous savons quel morceau de la requĂȘte correspond Ă  quel morceau du plan. Et dans ces morceaux, nous pouvons facilement et librement trouver les objets qui sont « lus ». Union RĂ©cursive.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Du point de vue de la requĂȘte, nous ne savons pas — s'il s'agit d'une table ou d'un CTE, mais ils sont dĂ©signĂ©s par le mĂȘme nƓud.

Tout est clair maintenant, nous savons quel segment de la requĂȘte correspond Ă  quel segment du plan. Et dans ces segments, nous pouvons facilement et naturellement identifier les objets qui sont "lisibles".

Du point de vue de la requĂȘte, nous ne savons pas s'il s'agit d'une table ou d'une CTE, mais ils sont tous dĂ©signĂ©s par le mĂȘme nƓud. PlageVar. Dans le plan, « se lit » Ă©galement comme un ensemble d'Ă©lĂ©ments assez limitĂ© :

  • Scan sĂ©quentiel sur [tbl]
  • Analyse de tas au format Bitmap sur [tbl]
  • Index [Only] Scan [Backward] utilisant [idx] sur [tbl]
  • Scan CTE sur [cte]
  • InsĂ©rer/Mise Ă  jour/Supprimer sur [tbl]

Nous connaissons la structure du plan et de la requĂȘte, nous savons quelles sont les correspondances des blocs, nous savons les noms des objets — nous faisons une correspondance sans ambiguĂŻtĂ©.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Encore une fois, la tĂąche « avec Ă©toile ». Nous prenons la requĂȘte, nous l'exĂ©cutons, nous n'avons pas d'alias — nous avons simplement lu deux fois un mĂȘme CTE.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Nous examinons le plan — quel est le problĂšme ? Pourquoi avons-nous un alias qui apparaĂźt ? Nous ne l'avons pas demandĂ©. D'oĂč vient-il, ce « numĂ©rotĂ© » ?

PostgreSQL l'ajoute lui-mĂȘme. Il faut simplement comprendre que cet alias prĂ©cis n'a aucune pertinence pour nous dans le but de la correspondance avec le plan, il a simplement Ă©tĂ© ajoutĂ© ici. Ne prĂȘtons pas attention Ă  lui.

DeuxiĂšme la tĂąche « avec Ă©toile »: si nous lisons depuis une table partitionnĂ©e, nous allons obtenir un nƓud Ajouter ou Fusion Append, qui sera composĂ© d'un grand nombre de « enfants », chacun d'eux Ă©tant un Scande type ‘ de la table-section : Scan sĂ©quentiel, Analyse de tas au format Bitmap ou Index Scan. Mais, dans tous les cas, ces « enfants » ne seront pas des requĂȘtes complexes — ainsi, ces nƓuds peuvent ĂȘtre distinguĂ©s des Ajouter lorsque UNION.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

De tels nƓuds, nous les comprenons Ă©galement, nous les rassemblons « en un tout » et nous disons : "tout ce que tu as lu de la megatable — c'est ici et vers le bas de l'arbre.".

Les nƓuds « simples » de rĂ©cupĂ©ration de donnĂ©es

PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Analyse des valeurs correspondent dans le plan VALEURS dans la requĂȘte.

RĂ©sultat — c'est une requĂȘte sans DE comme SELECT 1. Ou lorsque vous avez une expression forcĂ©ment fausse dans le OÙ-bloc (alors un attribut apparaĂźt One-Time Filter):

EXPLAIN ANALYZE
SELECT * FROM pg_class WHERE FALSE; -- ou 0 = 1

Result  (cost=0.00..0.00 rows=0 width=230) (actual time=0.000..0.000 rows=0 loops=1)
  One-Time Filter: false

Analyse de Fonction « s'alignent » sur des SRF homonymes.

Mais avec les sous-requĂȘtes, tout devient plus difficile — malheureusement, elles ne se transforment pas toujours en InitPlan/PlanSub. Parfois, elles se transforment en ... Join ou ... Anti Join, surtout lorsque vous Ă©crivez quelque chose comme WHERE NOT EXISTS .... Et lĂ , les combiner n'est pas toujours possible — dans le texte du plan, les nƓuds correspondants aux opĂ©rateurs dans le plan ne sont pas prĂ©sents.

Encore une fois, la tĂąche « avec Ă©toile »: plusieurs VALEURS dans la requĂȘte. Dans ce cas, dans le plan, vous obtiendrez plusieurs nƓuds. Analyse des valeurs.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Les « suffixes numĂ©rotĂ©s » aideront Ă  les distinguer les uns des autres — ils sont ajoutĂ©s exactement dans l'ordre de la dĂ©couverte des blocs correspondants de haut en bas. VALEURSIl semble que nous avons tout analysĂ© dans notre requĂȘte — il ne reste que

Traitement des données

Mais ici, c'est simple — des nƓuds comme Limite.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

« s'alignent » parfaitement sur les opĂ©rateurs correspondants dans la requĂȘte, s'ils y sont prĂ©sents. Ici, il n'y a pas de « Ă©toiles » ni de complications. Limite, Trier, AgrĂ©gat, WindowAgg, Unique Les difficultĂ©s apparaissent lorsque nous voulons combiner
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

JOIN

entre eux. Ce n'est pas toujours possible, mais c'est faisable. JOIN entre eux. Ce n'est pas toujours possible, mais c'est faisable.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Du point de vue du parseur de requĂȘtes, nous avons un nƓud ExprJoin, qui a exactement deux enfants - gauche et droit. C'est, respectivement, ce qui est «au-dessus» de votre JOIN et ce qui est «en dessous» dans la requĂȘte.

Et du point de vue du plan, c'est deux enfants d'un * Boucle/* Rejoignez-nƓud. Boucle imbriquĂ©e, Jointure anti-hachage,
 - quelque chose comme ça.

Utilisons une logique simple : si nous avons des tables A et B qui «s'unissent» entre elles dans le plan, alors dans la requĂȘte, elles peuvent ĂȘtre disposĂ©es soit A-JOIN-B, ou bien B-JOIN-A. Essayons de les assembler ainsi, essayons de les assembler Ă  l'envers, et ainsi de suite jusqu'Ă  ce que de telles paires soient Ă©puisĂ©es.

Prenons notre arbre syntaxique, prenons notre plan, regardons-les... cela ne ressemble pas!
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Redessinons-le sous forme de graphes - oh, cela commence déjà à ressembler à quelque chose!
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Faisons attention, nous avons des nƓuds qui ont en mĂȘme temps des enfants B et C - peu importe dans quel ordre. Assemblons-les et retournons l'image du nƓud.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Regardons encore une fois. Maintenant, nous avons des nƓuds avec des enfants A et la paire (B + C) - assemblons-les Ă©galement.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Super! Il s'avĂšre que nous avons rĂ©ussi Ă  assembler ces deux JOIN de la requĂȘte avec les nƓuds du plan.

Malheureusement, cette tùche n'est pas toujours résolue.
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Par exemple, si dans la requĂȘte A JOIN B JOIN C, et dans le plan, en premier, les nƓuds «extrĂȘmes» A et C se sont joints. Et il n'y a pas un tel opĂ©rateur dans la requĂȘte, nous n'avons rien Ă  mettre en surbrillance, rien auquel l'indice puisse ĂȘtre attachĂ©. La mĂȘme chose avec la «virgule», quand vous Ă©crivez A, B.

Mais, dans la plupart des cas, presque tous les nƓuds peuvent ĂȘtre «dĂ©liĂ©s» et obtenir ce type de profilage Ă  gauche selon le temps - littĂ©ralement, comme dans Google Chrome, lorsque vous analysez le code JavaScript. Vous voyez combien de temps chaque ligne et chaque opĂ©rateur ont «étĂ© exĂ©cutĂ©s».
PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Et pour que vous puissiez utiliser tout cela plus facilement, nous avons créé un stockage d'une archive, oĂč vous pouvez sauvegarder et ensuite retrouver vos plans avec les requĂȘtes associĂ©es ou partager le lien avec quelqu'un.

Si vous devez simplement formater une requĂȘte illisible de maniĂšre adĂ©quate, utilisez notre «normalizeur».

PostgreSQL Query Profiler : comment associer un plan et une requĂȘte

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster