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 Shto në UNION.
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

Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« đŸ”„ Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« | ProHoster