ShumĂ« prej atyre qĂ« tashmĂ« e pĂ«rdorin â shĂ«rbimin tonĂ« tĂ« vizualizimit tĂ« planeve PostgreSQL, ndoshta nuk e dinĂ« njĂ« nga superfuqitĂ« e tij â tĂ« kthente njĂ« copĂ« logu serveri me lexueshmĂ«ri tĂ« ulĂ«tâŠ

⊠në një kërkesë të bukur të strukturuar me sugjerime kontekstuale për nyjet përkatëse të planit:

Në këtë shpërthim të pjesës së dytë të do të tregoj se si arritëm ta bëjmë këtë.
Me transkriptin e pjesës së parë, e cila merret me problemet e zakonshme të performancës së kërkesave dhe zgjidhjet e tyre, mund të njiheni në artikullin .

SĂ« pari, do tĂ« merremi me ngjyrimin â dhe do ta ngjyrosim tani jo planin, sepse atĂ« e kemi bĂ«rĂ« tashmĂ« tĂ« bukur dhe kuptimplotĂ«, por kĂ«rkesĂ«n.
Na dukej qĂ« njĂ« «pllakë» e paformatizuar e nxjerrĂ« nga logu e kĂ«rkesĂ«s dukej shumĂ« e shĂ«mtuar dhe pĂ«r kĂ«tĂ« arsye â e pakĂ«ndshme.

Sidomos kur zhvilluesit në kod «ngjisin» trupin e kërkesës (sigurisht, kjo është një antipattern, por ndodh) në një rresht. Të tmerrshme!
Le të vizatojmë këtë në një mënyrë më të bukur.

Dhe nĂ«se mund ta vizatojmĂ« bukur, dmth. tĂ« analizojmĂ« dhe tĂ« rindĂ«rtojmĂ« trupin e kĂ«rkesĂ«s, atĂ«herĂ« mĂ« vonĂ« mund t'i lidhim çdo objekti tĂ« kĂ«saj kĂ«rkese me njĂ« sugjerim â çfarĂ« ndodhi nĂ« pikĂ«n pĂ«rkatĂ«se tĂ« planit.
Pema sintaksore e kërkesës
Për ta bërë këtë, së pari duhet të analizohet kërkesa.

Duke pasur parasysh se , ne e bĂ«mĂ« njĂ« modulik pĂ«r tĂ«, mund ta . NĂ« tĂ« vĂ«rtetĂ«, kjo Ă«shtĂ« njĂ« lidhje e zgjeruar me brendĂ«sitĂ« e parserit tĂ« vet PostgreSQL. Pra, thjesht Ă«shtĂ« njĂ« gramatikĂ« e kompilarizuar nĂ« mĂ«nyrĂ« binare dhe lidhje tĂ« bĂ«ra nga ana e NodeJS. Ne morĂ«m si bazĂ« module tĂ« huaja â kĂ«tu nuk ka asnjĂ« sekret tĂ« madh.
I japim trupin e kĂ«rkesĂ«s si hyrje nĂ« funksionin tonĂ« â dhe nĂ« dalje marrim pemĂ«n sintaksore tĂ« analizuar nĂ« formĂ«n e njĂ« objekti JSON.

Tani përmes kësaj peme mund të kalojmë në anën tjetër dhe të rindërtojmë kërkesën me ato tërheqje, ngjyrosje dhe formatizim që duam. Jo, kjo nuk është e konfiguruar, por na duket që pikërisht kështu do të ishte e përshtatshme.

Përputhja e nyjeve të kërkesës dhe planit
Tani do të shohim se si mund të kombinojmë planin, të cilin e analizuam në hapin e parë, dhe kërkesën, të cilën e analizuam në të dytin.
Le të marrim një shembull të thjeshtë - kemi një kërkesë që formon CTE dhe e lexon dy herë nga ajo. Ajo gjeneron një plan të tillë.

CTE
Nëse e shikojmë me kujdes, deri në versionin 12 (ose duke filluar nga ajo me fjalën kyçe MATERIALIZED) formimi .

Pra, nëse shohim diku në kërkesë gjenerimin e CTE dhe diku në plan një nyje CTE, atëherë këto nyje pa dyshim janë "të lidhura", mund t'i bashkojmë menjëherë.
Detyra "me yll": CTE mund të jenë të ndërlikuara.

Mund të jenë shumë keq të ndërlikuara, dhe madje me emra të njëjtë. Për shembull, ju mund të keni brenda CTE A të bëni CTE X, dhe në të njëjtin nivel brenda CTE B të bëni përsëri CTE X:
WITH A AS (
WITH X AS (...)
SELECT ...
)
, B AS (
WITH X AS (...)
SELECT ...
)
...Kur përputheni, duhet ta kuptoni këtë. Të kuptoni këtë "me sy" - madje duke parë planin, madje duke parë trupin e kërkesës - është shumë e vështirë. Nëse keni një gjenerim të CTE të ndërlikuar, të ndërlikuar, kërkesat janë të mëdha - atëherë nuk e kuptoni fare.
UNION
Nëse në kërkesën tonë ka fjalën kyçe UNION [ALL] (operatori i bashkimit të dy përzgjedhjeve), atëherë në plan i korrespondon ose një nyje Shto, ose ndonjë Bashkimi Rekursiv.

Ajo që "lartë" mbi UNION -është pasardhësi i parë i nyjës tonë, ajo që "poshtë" - e dyta. Nëse përmes UNION kemi "ngjitur" disa blloqe menjëherë, atëherë Shto-nyja do të jetë vetëm një, por fëmijët e saj do të jenë shumë - në përkatësi si shkojnë, përkatësisht:
(...) -- #1
UNION ALL
(...) -- #2
UNION ALL
(...) -- #3Append
-> ... #1
-> ... #2
-> ... #3
Detyra "me yll": brenda gjenerimit të zgjedhjes rekurzive (WITH RECURSIVE) gjithashtu mund të ketë më shumë se një UNION. Por gjithmonë rekurziv është vetëm blloku më i fundit pas UNION. Gjithçka që është lart - është një, por tjetër UNION:
WITH RECURSIVE T AS(
(...) -- #1
UNION ALL
(...) -- #2, këtu përfundon gjenerimi i gjendjes fillestare të rekurzionit
UNION ALL
(...) -- #3, vetëm ky bllok është rekurziv dhe mund të përmbajë një referencë ndaj T
)
... Shembuj të tillë gjithashtu duhet të dini si "të ndahen". Këtu, në këtë shembull, ne e shohim se UNION-se segmenteve në kërkesën tonë ishin 3. Pra, një UNION përputhet me Shto-nyjë, dhe një tjetër - Bashkimi Rekursiv.

Leximi-shkrimi i të dhënave
Tani, ne e kemi ndarë, tani e dimë se cila pjesë e kërkesës i përket cilës pjesë të planit. Dhe në këto pjesë ne mund të gjejmë lehtësisht dhe pa ndonjë vështirësi ato objekte që "lexohen".
Nga pikëpamja e kërkesës ne nuk e dimë - nëse është një tabelë apo CTE, por ato shënohen me të njëjtën nyje Shtrirja e Var. Ndërsa në planin "lexohet" - është gjithashtu një grup mjaft i kufizuar nyjesh:
Skene Sekuenciale në [tbl]Skanim i Shkëmbit Bitmap në [tbl]Indeksi [Vetëm] Skano [Mbrapsht] duke përdorur [idx] mbi [tbl]Skano CTE në [cte]Shtoni/Përditëso/Delete në [tbl]
Struktura e planit dhe e kërkesës e dimë, përputhjen e blloqeve e dimë, emrat e objekteve i dimë - bëjmë një përputhje të qartë.

Sërish, detyra "me yll". Marrim kërkesën, e realizojmë, nuk kemi asnjë alias - thjesht të dyja herët kemi lexuar nga një CTE.

Shikojmë në plan - çfarë ndodhi? Pse na doli një alias? Nuk e kishim kërkuar. Si doli ai "me numër"?
PostgreSQL e shton vetë atë. Duhet thjesht të kuptojmë se pikërisht ky alias nuk ka asnjë kuptim për ne për qëllime përputhjeje me planin, ai është thjesht këtu për t'u shtuar. Të mos i kushtojmë vëmendje.
I dyti detyra "me yll": nëse kemi lexim nga një tabelë të ndarë, atëherë do të kemi një nyje Shto ose Bashkosh Bashkëngjit, e cila do të përbëhet nga një numër i madh "fëmijësh" dhe secili prej tyre do të jetë një lloj Scan'i tabelës-seksion: Skene Sekuenciale, Skanim i Shkëmbit Bitmap ose Index Scan. Por, në çdo rast, këta "fëmijë" do të jenë kërkesa jo të komplikuara - kështu që këto nyje mund të dallohen nga Shto në UNION.

Këto nyje gjithashtu i kuptojmë, i grumbullojmë "në një grup" dhe themi: "çdo gjë që ke lexuar nga megatable - është këtu dhe poshtë në pemë.".
"Nyjet "e thjeshta" të marrjes së të dhënave

Skemë Vlerash në plan përputhen VLERAT në kërkesë.
Rezultati - kjo është një kërkesë pa FROM siç duket SELECT 1. Ose kur keni një shprehje të gabuar në KU-bllok (atëherë lind atributi One-Time Filter):
EXPLAIN ANALYZE
SELECT * FROM pg_class WHERE FALSE; -- ose 0 = 1Rezultati (kostot=0.00..0.00 rreshta=0 gjerësi=230) (koha aktuale=0.000..0.000 rreshta=0 loops=1)
One-Time Filter: false
Skandimi i Funksionit "mapohen" në SRF të njëjtë.
Por me kërkesat e thelluara është më e komplikuar - fatkeqësisht, ato nuk transformohen gjithmonë në PlaniFill/SubPlan. Ndonjëherë ata transformohen në ... Join ose ... Anti Join, veçanërisht kur shkruani diçka si WHERE NOT EXISTS .... Dhe aty kombinimi nuk është gjithmonë i mundur - në tekstin e planit operatoret përkatës nuk ekzistojnë.
Sërish, detyra "me yll": disa VLERAT në kërkesë. Në këtë rast dhe në plan do të merrni disa nyje Skemë Vlerash.

Të dallohet njëri nga tjetri do të ndihmojnë sufikset "numerik" - ato shtohen saktësisht në rendin e gjetjes së përputhjeve VLERAT-blloqeve përgjatë kërkesës nga lartë poshtë.
Përpunimi i të dhënave
Duket se kemi shkruar gjithçka në kërkesën tonë - mbeti vetëm Limit.

Por këtu është thjesht - këto nyje si Limit, Rregullo, Shkallëzimi, WindowAgg, Unike "mapohen" një-në-një me operatoret përkatës në kërkesë, nëse ato janë atje. Nuk ka "ylli" dhe vështirësi këtu.

JOIN
Vështirësitë lindin, kur duam të përshtatim JOIN ndërmjet tyre. Kjo nuk është gjithmonë e mundur, por është e mundur.

Nga pikĂ«pamja e parser-it tĂ« pyetjeve, kemi njĂ« nyje JoinExpr, e cila ka pikĂ«risht dy pasardhĂ«s â tĂ« majtĂ« dhe tĂ« djathtĂ«. Kjo, pĂ«rkatĂ«sisht, Ă«shtĂ« ajo qĂ« Ă«shtĂ« "nĂ«" JOIN tuaj dhe ajo qĂ« Ă«shtĂ« "nĂ«n" tĂ« nĂ« pyetje.
Dhe nga pikĂ«pamja e planit, janĂ« dy pasardhĂ«s tĂ« ndonjĂ« * Rreth/* Bashkohu-nyje. Nested Loop, Hash Anti Join,⊠â diçka e tillĂ«.
Le të përfitojmë nga logjika e thjeshtë: nëse kemi tabela A dhe B, të cilat "bashkohen" mes tyre në plan, atëherë në pyetje ato mund të jenë vendosur ose A-JOIN-B, ose B-JOIN-A. Le të provojmë t'i kombinojmë kështu, le të provojmë t'i kombinojmë atëherë, dhe kështu deri sa nuk mbarojnë këto çifte.
Le të marrim pemën tonë sintaktike, le të marrim planin tonë, le të shohim mbi ta⊠nuk duket!

TĂ« rifreskojmĂ« si grafĂ« â o, tani tashmĂ« po duket si diçka!

Le tĂ« vĂ«mĂ« re se kemi nyje, tĂ« cilat njĂ«kohĂ«sisht kanĂ« fĂ«mijĂ« B dhe C â nuk ka rĂ«ndĂ«si nĂ« cilin rend. Le tĂ« bashkojmĂ« dhe tĂ« kthejmĂ« imazhin e nyjĂ«s.

Le tĂ« shohim pĂ«rsĂ«ri. Tani kemi nyje me fĂ«mijĂ« A dhe çiftin (B + C) â le tĂ« bashkojmĂ« edhe ato.

Përkryer! Kështu del se këto dy JOIN nga pyetja me nyjet e planit i kemi kombinuar me sukses.
Fatkeqësisht, kjo detyrë nuk zgjidhet gjithmonë.

Për shembull, nëse në pyetje A JOIN B JOIN C, dhe në plan në radhë të parë janë bashkuar nyjet "ekstreme" A dhe C. Dhe në pyetje s'ka një operator të tillë, nuk kemi asgjë për të nxjerrë në pah, asgjë për të lidhur me sugjerimin. E njëjta gjë ndodh me "virgjëreshën", kur shkruani A, B.
Por, nĂ« shumicĂ«n e rasteve, gati tĂ« gjitha nyjet mund tĂ« "zhbllokohen" dhe tĂ« marrim njĂ« profilizim tĂ« tillĂ« majtas sipas kohĂ«s â pikĂ«risht si nĂ« Google Chrome, kur analizoni kodin nĂ« JavaScript. Shihni sa kohĂ« ka kaluar çdo rresht dhe çdo operator "u ekzekutua".

Dhe për ta bërë më të lehtë për ju të përdorni të gjitha këto, ne kemi bërë ruajtjen , ku mund të ruani dhe më pas të gjeni planet tuaja së bashku me pyetjet e asociuara ose të ndani një lidhje me dikë.
Nëse keni nevojë thjesht për ta sjellë një pyetje jo të lexueshme në një formë të përshtatshme, përdorni .

Burimi: habr.com
