PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Shumë nga ata që tashmë po e përdorin explain.tensor.ru — shërbimin tonë të vizualizimit të planeve PostgreSQL, ndoshta nuk e dinë një nga superfuqitë e tij — të shndërrojë një pjesë të logut të serverit që është e vështirë për t'u lexuar...

PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën
... në një kërkesë të përgatitur bukur me këshilla kontekstuale për nyjet përkatëse të planit:

PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën
Në këtë interpretim të pjesës së dytë të raportit tim në PGConf.Russia 2020 unë do të flas për mënyrën si arritëm ta bëjmë këtë.

Me transkriptin e pjesës së parë, e cila ishte e dedikuar problemeve tipike të performancës së kërkesave dhe zgjidhjeve të tyre, mund të njiheni në artikullin «Recetat për kërkesat SQL që kanë nevojë».


Luaj videon

Fillimisht do të merremi me ngjyrat — dhe do të ngjyrosim jo planin, sepse ne e kemi ngjyrosur atë, tashmë është i bukur dhe i kuptueshëm, por do të merremi me kërkesën.

Na dukej se një «tepih» i tillë i paformatizuar që është nxjerrë nga logu duket shumë i shëmtuar dhe prandaj — i pakëndshëm.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Veçanërisht kur zhvilluesit në kod «bashkojnë» trupin e kërkesës (kjo, sigurisht, është një antipattern, por ndodh) në një rresht. Tmerre!

Le ta vizatojmë këtë më bukur.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Nëse arrijmë ta vizatojmë bukur këtë, domethënë, të analizojmë dhe riparojmë trupin e pyetjes, atëherë mund të ngjitim një sugjerim për çdo objekt të kësaj pyetjeje — se çfarë ndodhi në pikën përkatëse të planit.

Pema sintaksore e pyetjes

Për ta bërë këtë, së pari duhet të analizojmë pyetjen.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Duke qenë se ne në kernelin e sistemit punojmë me NodeJS, kemi krijuar një modul për të, mund ta gjeni në GitHub. Në të vërtetë, kjo janë binding të zgjeruara për brendësitë e parserit të PostgreSQL. Domethënë, është thjesht një gramatikë e përkthyer në format binar dhe janë krijuar binding me NodeJS. Ne përdorëm module të huaja si bazë — nuk ka asnjë sekret të madh këtu.

Futur trupin e pyetjes në funksionin tonë — në daljen marrim pemën sintaksore të analizuara në formën e një objekti JSON.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Tani, mbi këtë pemë mund të kalojmë në anën tjetër dhe të rindërtojmë pyetjen me ato hapësira, ngjyrimin, formatimin që dëshirojmë. Jo, kjo nuk është e konfiguruar, por na duket se kështu do të ishte e përshtatshme.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Krahasimi i nyjeve të pyetjes dhe planit

Tani do ta shohim se si mund të kombinojmë planin që ne e shqyrtuam në hapin e parë dhe kërkesën që ne e shqyrtuam në hapin e dytë.

Le të marrim një shembull të thjeshtë — kemi një kërkesë që formon CTE dhe e lexon dy herë nga ajo. Ai gjeneron një plan të tillë.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

CTE

Nëse i hedhim një vështrim të afërt, që nga versioni 12 (ose duke filluar nga ai me fjalën kyçe MATERIALIZED) formimi CTE është një barrierë e pamohueshme për planifikuesin..
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Pra, nëse shohim diku në kërkesë gjenerimin e CTE dhe diku në plan një nyje CTE, atëherë këto nyje ndërlidhen me njëra-tjetrën dhe ne mund t'i kombinojmë ato menjëherë.

Detyra "me yll": CTE mund të jenë të ngjitura.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën
Janë disa që janë shumë keq të ngjitura, madje edhe të emërtuara njësoj. Për shembull, ju mund të ndani CTE A të bëni CTE X, dhe në të njëjtin nivel brenda CTE B të bëni përsëri CTE X:

ME A SI (
  ME X SI (...)
  Zgjedh ...
)
, B SI (
  ME X SI (...)
  Zgjedh ...
)
...

Kur krahasoni, duhet ta kuptoni këtë. Ta kuptoni me "sytë" — edhe duke parë planin, edhe duke parë trupin e kërkesës — është shumë e vështirë. Nëse keni një gjenerim të CTE të ndërlikuar, të ngjitur, kërkesat janë të mëdha — atëherë ndjenja është krejtësisht e pavetëdijshme.

UNION

Nëse kemi një fjalë kyçe në kërkesë UNION [ALL] (operatori i lidhjes midis dy grupeve), atëherë ai në plan i korrespondon ose një nyje Shto, ose ndonjë Bashkimi Rekursiv.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Ajo që "sipër" mbi UNION — është pasardhësi i parë i nyjës tonë, ndërsa "poshtë" — i dyti. Nëse përmes UNION kemi "në ngjitje" disa blloqe menjëherë, atëherë Shto-nyja do të mbetet vetëm një, por fëmijët e saj do të jenë jo dy, por shumë — në rendin siç vijnë, përkatësisht:

  (...) -- #1
UNION ALL
  (...) -- #2
UNION ALL
  (...) -- #3

Shto
  -> ... #1
  -> ... #2
  -> ... #3

Detyra "me yll": brenda gjenerimit të zgjedhjes rekursive (WITH RECURSIVE) gjithashtu mund të ketë më shumë se një UNION. Por gjithmonë rekursive është vetëm blloku më i fundit pas të fundit UNION. Çdo gjë që është lart — është një, por tjetër UNION:

ME RECURSIVE T AS(
  (...) -- #1
UNION ALL
  (...) -- #2, këtu përfundon gjenerimi i gjendjes fillestare të rekursioneve
UNION ALL
  (...) -- #3, vetëm ky bllok është rekursiv dhe mund të përmbajë referenca te T
)
...

Këto shembuj duhet gjithashtu të dinë "të ngjiten". Në këtë shembull ne shohim se UNION-segmenteve në kërkesën tonë ishin 3 copa. Përkatësisht, njëri UNION përputhet Shto-nyjë, ndërsa tjetri — Bashkimi Rekursiv.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Leximi-shkrimi i të dhënave

Të gjitha, e organizuam, tani e dimë se cili pjesë e kërkesës i përket cilit pjesë të planit. Dhe në këto pjesë ne mund të gjejmë lehtësisht objektet që "lexohen".

Nga këndvështrimi i kërkesës ne nuk e dimë — është tabelë apo CTE, por ato karakterizohen nga e njëjta nyje. RangeVar. Ndërsa në planin "lexohet" — gjithashtu është një grup mjaft të kufizuar nyjesh:

  • Skemë e sekuencës në [tbl]
  • Skani i Bitmap Heap në [tbl]
  • Indeksi [Vetëm] Skano [Mbrapsht] duke përdorur [idx] në [tbl]
  • Skano CTE në [cte]
  • Futni/Update/Fshi në [tbl]

Struktura e planit dhe e kërkesës e dimë, përputhja e blloqeve e dimë, emrat e objekteve i dimë — bëjmë një përputhje të qartë.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Përsëri, detyra "me yll". Marrim kërkesën, e ekzekutojmë, nuk kemi asnjë alias — ne thjesht lexuam dy herë nga një CTE.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Shikojmë në plan — çfarë ndodhi? Pse na doli aliasi? Ne nuk e kërkuam. Nga erdhi ky "numer"?

PostgreSQL e shton atë vetë. Duhet thjesht të kuptojmë se pikërisht ky alias për ne për qëllimet e përputhjes me planin nuk ka asnjë kuptim, ai është thjesht këtu i shtuar. Mos ta vëmë re atë.

E dyta detyra "me yll": nëse kemi lexim nga një tabelë të ndarë, atëherë do të marrim një nyjë Shto ose Bashko Shto, e cila do të përbëhet nga një numër i madh "fëmijësh", dhe secila do të jetë një Scan' nga tabela-seksion: Skemë e sekuencës, Skani i Bitmap Heap ose Index Scan. Por megjithatë, këto "fëmijë" do të jenë kërkesa të thjeshta - kështu që këto nyje mund të dallohen nga ShtoUNION.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Këto nyje ne i kuptojmë gjithashtu, i grupojmë "në një grumbull" dhe themi: "gjithçka që ke lexuar nga megatable - është këtu dhe poshtë në pemë".

"Nyjet e thjeshta" për marrjen e të dhënave

PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Skano të Dhënash përputhet me VALUES në kërkesë.

Shkalla - kjo është një kërkesë pa FROM si SELECT 1. Ose kur ke një shprehje të gënjyer në WHERE-bllok (atëherë krijohet atributi One-Time Filter):

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

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

Skano Funksional "mapojen" në SRF të njëjtë.

Por për kërkesat e brendshme gjërat janë më të komplikuara - fatkeqësisht, ato nuk transformohen gjithmonë në PlaniFill/SubPlani. Herë pas here ato kthehen në ... Join ose ... Anti Join, sidomos kur shkruani diçka si WHERE NOT EXISTS .... Dhe aty kombinimi nuk është gjithmonë i mundur - në tekstin e planit nuk ka nyje përkatëse të operatorëve.

Përsëri, detyra "me yll": disa VALUES në kërkesë. Në këtë rast, dhe në plan do të merrni disa nyje Skano të Dhënash.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Dallimi i tyre do të ndihmohet nga sufiksat "numërorë" - ai shtohet saktësisht në rendin e gjetjes së VALUES-bllokëve gjatë kërkesës nga lartë poshtë.

Përpunimi i të dhënave

Duket se kemi trajtuar gjithçka në kërkesën tonë — ka mbetur vetëm Kufizim.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Por këtu gjithçka është e thjeshtë — nyjet si Kufizim, Rendit, Agregoni, WindowAgg, Unike «mapohet» një-për-një me operatorët përkatës në kërkesë, nëse ata janë aty. Nuk ka asnjë «yll» dhe as vështirësi.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

JOIN

Vështirësitë ndodhin kur duam të kombinojmë JOIN me njëri-tjetrin. Kjo nuk bëhet gjithmonë, por është e mundur.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Nga perspektiva e parserit të kërkesës, kemi një nyje BashkohuExpr, e cila ka pikërisht dy pasardhës — të majtë dhe të djathtë. Kjo, përkatësisht, është ajo që është «mbi» JOIN tuaj dhe ajo që është «në» të shkruarit në kërkesë.

Dhe nga perspektiva e planit, kjo janë dy pasardhës të një * Rreth/* Bashkëngjitu-nyje. Nested Loop, Hash Anti Join,… — diçka e tillë.

Të shfrytëzojmë logjikën e thjeshtë: nëse kemi tabelat A dhe B, të cilat «bashkohen» me njëra-tjetrën në plan, atëherë ato mund të kenë qenë të vendosura në kërkesë si A-JOIN-B, ose B-JOIN-A. Le t'i kombinojmë kështu, le t'i kombinojmë për ndryshe, dhe kështu deri sa të përfundojnë këto çifte.

Të marrim pemën tonë sintaksore, të marrim planin tonë, t'i shohim ato… nuk duket!
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Ta vizatojmë atë në formë grafikësh — o, tashmë duket diçka që i ngjan diçkaje!
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Le të vëmë re se kemi nodet që kanë njëkohësisht fëmijë B dhe C — nuk ka rëndësi në cilin rend. Do t'i bashkojmë dhe do ta kthejmë imazhin e nodit.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Të shohim përsëri. Tani kemi nodet me fëmijë A dhe çiftet (B + C) — do t'i bashkojmë edhe këta.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Shkëlqyeshëm! Del se këto dy JOIN në kërkesat me nodet e planit i kemi bashkuar me sukses.

Fatkeqësisht, kjo detyrë nuk zgjidhet gjithmonë.
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Për shembull, nëse në kërkesë A JOIN B JOIN C, dhe në plan iu bashkuan në radhë nodet "ekstreme" A dhe C. Dhe në kërkesë nuk ka një operator të tillë, nuk kemi asgjë për të theksuar, nuk ka për çfarë të lidhim sugjerimin. E njëjta gjë ndodh me "presjen", kur shkruani A, B.

Por, në shumicën e rasteve, pothuajse të gjitha nodet arrijnë të "zgjidhen" dhe të marrin këtë profilim në të majtë sipas kohës — dosido, si në Google Chrome kur analizoni kodin në JavaScript. Shihni sa kohë e kishte çdo rresht dhe çdo operator "ekzekutuar".
PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Dhe për ta bërë më të lehtë përdorimin tuaj me të gjitha këto, ne kemi bërë ruajtje arkiv, ku mund të ruani dhe më pas të gjeni planet tuaja së bashku me kërkesat e asociuara ose të ndani një lidhje me dikë.

Nëse thjesht duhet ta çoni një kërkesë të paqartë në një formë të arsyeshme, përdorni normalizuesi ynë.

PostgreSQL Query Profiler: si të përshtatni planin dhe kërkesën

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster