Daudzi, kuri jau izmanto — mūsu PostgreSQL plānu vizualizācijas servisu, iespējams, nav informēti par vienu no tā superspējām — pārvērst grūti saprotamu servera žurnāla daļu...

… skaisti noformētā vaicājumā ar konteksta norādēm par attiecīgajiem plāna mezgliem:

Šajā pārskatā otra daļa no mans es pastāstīšu, kā mums izdevās to izdarīt.
Ar pirmās daļas transkriptu, kas veltīts tipiskajām vaicājumu veiktspējas problēmām un to risinājumiem, var iepazīties rakstā .

Vispirms pievērsīsimies krāsošanai — un krāsosim jau ne plānu, jo to mēs jau esam izkrāsojuši, tas ir mūsu jau skaists un saprotams, bet vaicājumu.
Mums šķita, ka šādi neformatēts «palags», izvilkts no žurnāla, izskatās ļoti nelaikā un tādēļ — neērti.

īpaši, kad izstrādātāji kodā «pieliek» vaicājuma ķermeni (tas, protams, ir antipattern, bet gadās) vienā rindā. Šausmas!
Iesāksim šo zīmēt kaut kā skaistāk.

Un, ja mēs spēsim to zīmēt skaisti, t.i., izpētīt un atkal salikt vaicājuma ķermeni, tad pēc tam varēsim pie katra objekta šajā vaicājumā «piestiprināt» norādi — kas notika attiecīgajā plāna punktā.
Vaicājuma sintaktiskā koka
Lai to izdarītu, vaicājumu vispirms jāizpēta.

Tā kā mūsu , mēs uzsākām tam moduli, varat . Patiesībā tas ir paplašinātie «bindiņi» uz PostgreSQL parsēra iekšējo struktūru. Tātad tas ir vienkārši bināri kompilēta gramatika un pie tās ir veiktas NodeJS saites. Mēs balstījāmies uz citu moduļu kodu — šeit nav nekādas lielas noslēpumainības.
Mēs ieejam vaicājuma ķermeni mūsu funkcijā — izeja ir izpētīts sintaktiskais koks JSON objekta formā.

Tagad pa šo koku varam iet atpakaļ un salikt vaicājumu ar tiem atstarpēm, krāsojumu, formatējumu, kādus vēlamies. Nē, tas nav konfigurējams, bet mums šķita, ka tieši tā būtu ērti.

Vaicājuma mezglu un plāna salīdzinājums
Tagad aplūkosim, kā varam apvienot plānu, ko mēs izpētījām pirmajā solī, un vaicājumu, ko izpētījām otrajā.
Apskatīsim vienkāršu piemēru — mums ir vaicājums, kas veido CTE un divas reizes no tās lasa. Tas ģenerē tādu plānu.

CTE
Ja paskatāmies uz to uzmanīgi, tad līdz 12. versijai (vai sākot ar to ar atslēgvārdu MATERIALIZED) veidošana .

Tas nozīmē, ja mēs redzam kaut kur vaicājumā CTE ģenerāciju un kaut kur plānā mezglu CTE, tad šie mezgli noteikti "saskaras" viens ar otru, mēs varam tos uzreiz saskaņot.
Uzdevums „ar zvaigznīti”: CTE var būt iegultas.

Tās var būt ļoti slikti iegultas, un pat ar to pašu nosaukumu. Piemēram, jūs varat iekšā CTE A izveidot CTE X, un tajā pašā līmenī iekšā CTE B atkal izveidot CTE X:
WITH A AS (
WITH X AS (...)
SELECT ...
)
, B AS (
WITH X AS (...)
SELECT ...
)
...Saskaņojot, jums tas jāsaprot. Saprast to „ar acīm” — pat redzot plānu, pat redzot vaicājuma ķermeni — ir ļoti grūti. Ja jums ir sarežģīta, iegulta CTE ģenerācija, lieli vaicājumi — tad tas ir vispār neapzināti.
UNION
Ja mūsu vaicājumā ir atslēgvārds UNION [VISU] (divu datu kopu savienošanas operators), tad tam plānā atbilst vai nu mezgls Pievienot, vai kaut kas tāds Rekursīvā savienība.

Tas, kas „augšā” virs UNION — tas ir mūsu mezgla pirmais pēcnācējs, bet „apakšā” — otrais. Ja caur UNION mums ir „pievienoti” vairāki bloki uzreiz, tad Pievienot-mezgls tomēr būs tikai viens, bet bērnu būs nevis divi, bet daudz — secībā, kā tie seko:
(...) -- #1
UNION ALL
(...) -- #2
UNION ALL
(...) -- #3Pievienojiet
-> ... #1
-> ... #2
-> ... #3
Uzdevums „ar zvaigznīti”: iekšējā rekursīvā izvēlē (WITH RECURSIVE) arī var būt vairāk nekā viens UNION. Bet vienmēr rekursīvā ir tikai pēdējais bloks pēc pēdējā UNION. Viss, kas augstāk — tas ir viens, bet cits UNION:
WITH RECURSIVE T AS(
(...) -- #1
UNION ALL
(...) -- #2, šeit beidzas sākotnējā rekursijas stāvokļa ģenerācija
UNION ALL
(...) -- #3, tikai šis bloks ir rekursīvs un var saturēt atsauci uz T
)
... Šādus piemērus arī jāspēj „izmanīt”. Šajā piemērā mēs redzam, ka UNION-segmenti mūsu vaicājumā bija 3 gabali. Attiecīgi vienam UNION atbilst Pievienot-mezgls, bet otram — Rekursīvā savienība.

Datu lasīšana un rakstīšana
Visu esam izklāstījuši, tagad mēs zinām, kurš vaicājuma gabals atbilst kuram plāna gabalam. Un šajos gabalos mēs varam viegli un brīvi atrast tos objektus, kas „tiek lasīti”.
Vaicājuma kontekstā mēs nezinām — vai tas ir tabula vai CTE, bet tie tiek apzīmēti ar to pašu mezglu. RangeVar. Bet pie šī plāna „lasāmības” – tas ir arī diezgan ierobežots mezglu kopums:
Secīga skenēšana uz [tbl]Bitmapu kaudzes skenēšana uz [tbl]Indekss [Only] Skenēt [Atpakaļ] izmantojot [idx] uz [tbl]CTE skanējums uz [cte]Ievietot/Atjaunināt/Dzēst uz [tbl]
Mēs zinām plāna un pieprasījuma struktūru, zinām bloku atbilstību, zinām objektu nosaukumus – veicam viennozīmīgu atbilstību.

Atkal uzdevums „ar zvaigznīti”. Mēs ņemam pieprasījumu, izpildām, mums nav nekādu aliasu – mēs vienkārši divas reizes lasījām no viena CTE.

Paskatāmies plānā – kas noticis? Kāpēc mums parādījās alias? Mēs to nepasūtījām. No kurienes viņš ir nācis „numurs”?
PostgreSQL to pievieno automātiski. Jāvienkārši saprot, ka tieši tāds alias mūsu atbilstības plānam nolūkos neietver nekādu jēgu, tas ir vienkārši pievienots. Nevērsīsim uz to uzmanību.
Otrais uzdevums „ar zvaigznīti”: ja mums ir lasīšana no sekcionētās tabulas, tad mēs saņemsim mezglu Pievienot vai Apvienot Pievienot, kas sastāvēs no liela skaita „bērnu”, un katrs no tiem būs kāds Scan‘s no tabulas-sekcijas: Secīga skenēšana, Bitmapu kaudzes skenēšana vai Index Scan. Bet, katrā ziņā, šie „bērni” nebūs sarežģīti pieprasījumi – tādus mezglus var atšķirt no Pievienot kad UNION.

Šādus mezglus mēs arī saprotam, savācam „vienā kaudzē” un sakām: "viss, ko tu lasīji no megatable — tas ir šeit un uz leju caur koku".
„Vienkāršie” mezgli datu iegūšanai

Vērtību skenēšana plānā atbilst VALUES pieprasījumā.
Rezultāts — tā ir pieprasījums bez FROM tā it kā SELECT 1. Vai kad jums ir acīmredzami nepatiesa izteiksme WHERE-blokā (tad rodas atribūts One-Time Filter):
EXPLAIN ANALYZE
SELECT * FROM pg_class WHERE FALSE; -- vai arī 0 = 1Rezultāts (izmaksas=0.00..0.00 rinda=0 platums=230) (faktiskais laiks=0.000..0.000 rindas=0 cikli=1)
One-Time Filter: false
Funkcijas skenēšana „mapējas” uz vienādajām SRF.
Bet ar iegulto pieprasījumiem viss ir sarežģītāk – diemžēl tie ne vienmēr pārvēršas par Sākuma plāns/SubPlāns. Dažreiz tie pārvēršas par ... Join vai ... Anti Join, it īpaši, kad jūs rakstāt kaut ko līdzīgu WHERE NOT EXISTS .... Un tur apvienot ne vienmēr izdodas – plāna tekstā atbilstošiem mezglu operatoriem nav.
Atkal uzdevums „ar zvaigznīti”: vairākas VALUES pieprasījumā. Šajā gadījumā arī plānā jūs saņemsiet vairākus mezglus Vērtību skenēšana.

Atšķirt tos vienu no otra palīdzēs „numura” piedēkļi – tas tiek pievienots tieši saskaņā ar atbilstošo VALUES-bloku atrašanas secībā no augšas uz leju.
Datu apstrāde
Izskatās, ka visu mūsu pieprasījumā esam izskatījuši – palicis tikai Limit.

Bet šeit viss ir vienkārši – tādi mezgli kā Limit, Kārtot, Agregāts, WindowAgg, Unique „mapējas” viens pret vienu uz atbilstošiem operatoriem pieprasījumā, ja tie tur ir. Šeit nekādu „zvaigžņu” un sarežģījumu nav.

JOIN
Sarežģījumi rodas, kad mēs gribam saskaņot JOIN savā starpā. To izdarīt ne vienmēr ir iespējams, bet var.

No skatījumā par vaicājumu parsēšanu mums ir mezgls PievienotiesExpr, kam ir tieši divi bērni — kreisais un labais. Tas ir, attiecīgi, tas, kas ir
virs * Cikls/* Pievienoties-mezgla. Iekšējā cilpa, Hash Anti Join,… — kaut kas līdzīgs.
Izmantojam vienkāršu loģiku: ja mums ir tabulas A un B, kas tiek savienotas A-JOIN-B, vai B-JOIN-A. Mēģināsim savienot tā, mēģināsim savienot pretēji, un tā līdz šādās pāros beigsies.
Apskatīsim mūsu sintaktisko koku, apskatīsim mūsu plānu, paskatīsimies uz tiem… neizskatās līdzīgi!

Pārzīmēsim attēlus grafos — o, jau izskatās kā kaut kas!

Pievērsīsim uzmanību, ka mums ir mezgli, kuriem vienlaikus ir bērni B un C — mums nav svarīgi kādā secībā. Savienojam tos un mainām mezgla attēlu.

Paskatīsimies vēlreiz. Tagad mums ir mezgli ar bērniem A un pāri (B + C) — savienojam arī tos.

Lieliski! Izrādās, ka mēs šos divus JOIN no vaicājuma ar plāna mezgliem veiksmīgi savienojām.
Diemžēl šo uzdevumu ne vienmēr var atrisināt.

Piemēram, ja vaicājumā A JOIN B JOIN C, bet plānā vispirms savienojās "galējie" mezgli A un C. Un vaicājumā nav tāda operatora, mēs nevaram izcelt neko, nav kur piesiet norādi. Tas pats attiecībā uz "komatu", kad jūs rakstāt A, B.
Bet lielākajā daļā gadījumu gandrīz visus mezglus izdodas "atšķetināt" un iegūt šādu profilēšanu kreisajā pusē pa laiku — burtiski, kā Google Chrome, kad jūs analizējat JavaScript kodu. Jūs redzat, cik ilgi katra rinda un katrs operators "tika izpildīts".

Un, lai jums būtu ērti izmantot visu šo, mēs izveidojām glabāšanu , kur jūs varat saglabāt un pēc tam atrast savus plānus kopā ar saistītajiem vaicājumiem vai dalīties ar kādu saiti.
Ja jums vienkārši jāieved nesaprotams vaicājums saprātīgā veidā, izmantojiet .

Avots: habr.com
