Beaucoup de ceux qui utilisent dĂ©jĂ â 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âŠ

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

Dans cette analyse de la deuxiÚme partie de sa 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 .

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.

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.

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.

Puisque notre , nous avons créé un module, vous pouvez . 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.

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.

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.

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 .

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.

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.

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.

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Ă©.

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.

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.

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

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 = 1Result (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.

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.

« 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

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.

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!

Redessinons-le sous forme de graphes - oh, cela commence déjà à ressembler à quelque chose!

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.

Regardons encore une fois. Maintenant, nous avons des nĆuds avec des enfants A et la paire (B + C) - assemblons-les Ă©galement.

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.

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».

Et pour que vous puissiez utiliser tout cela plus facilement, nous avons créé un stockage , 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 .

Source : habr.com
