PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Paljuski, kes juba kasutavad explain.tensor.ru — meie PostgreSQL plaanide visualiseerimise teenust, ei pruugi teada selle ĂŒhe erilise omaduse ĂŒle — muuta keeruliselt loetav serveri logitĂŒkk...

PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada
...ilusalt vormindatud pÀringuks kontekstuaalsete vihjete ja vastavate plaanisÔlmede kohta:

PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada
Selles teises osas oma ettekandest PGConf.Russia 2020 rÀÀgin, kuidas me seda tegime.

Esimese osa transkriptsiooniga, mis kĂ€sitleb tĂŒĂŒpilisi pĂ€ringute tulemuslikkuse probleeme ja nende lahendusi, saab tutvuda artiklis „Retseptid haige SQL-pĂ€ringu jaoks”.


Vaata videot

Alustame vĂ€rvimisest — ja vĂ€rvime teemat, mitte plaani, oleme selle juba ilusaks ja arusaadavaks teinud.

Meie arvates nĂ€eb niimoodi vormindamata „lapp“, logist vĂ€lja tĂ”mmatud pĂ€ring, vĂ€ga kole vĂ€lja ja seetĂ”ttu — ebamugav.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Eriti kui arendajad „kleepivad“ pĂ€ringu keha koodi sisse (see on muidugi antipattern, aga juhtub) ĂŒhte ritta. Kohutav!

Teeme selle kuidagi ilusamalt.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Ja kui me saame selle ilusasti kujundada, st lahti vĂ”tta ja tagasi kokku panna pĂ€ringute keha, siis suudame igale selle pĂ€ringu objektile "kinnitada" vihje — mis juhtus vastavas planeeringu punktis.

PĂ€ringu sĂŒntaktiline puu

Selleks tuleb pĂ€ring kĂ”igepealt analĂŒĂŒsida.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Kuna meil on sĂŒsteemi tuumik töötamas NodeJS-is, siis tegime sellele mooduli, mille leiate GitHubist. Tegelikult on see laienenud "sidemed" PostgreSQL-i parseri sisemustega. See tĂ€hendab, et grammaatika on lihtsalt binaarselt kompileeritud ja sellele on tehtud sidemed NodeJS-i poolt. VĂ”tsime aluseks teiste moodulid — siin pole suurt saladust.

Söödame pĂ€ringu keha meie funktsiooni sisendisse — vĂ€ljundina saame analĂŒĂŒsitud sĂŒntaktilise puu JSON-objekti kujul.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

NĂŒĂŒd saab seda puud töödelda vastupidises suunas ja koguda pĂ€ringut selliste indentide, vĂ€rvimise ja vormindamisega, nagu me soovime. Ei, seda ei saa seadistada, kuid tundus, et just nii on mugav.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

PÀringu sÔlmede ja plaani vastavus

NĂŒĂŒd vaatame, kuidas saame ĂŒhendada plaani, mille me esimeses etapis lahkasime, ja pĂ€ringu, mille me teises etapis analĂŒĂŒsisime.

VĂ”tame lihtsa nĂ€ite — meil on pĂ€ring, mis genereerib CTE ja loeb seda kaks korda. See genereerib sellise plaani.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

CTE

Kui sellele tÀhelepanelikult vaadata, siis enne 12. versiooni (vÔi alates sellest koos mÀrksÔnaga MATERIALIZED) on CTE genereerimine ilmtingimata takistuseks plaanijale..
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Seega, kui me nĂ€eme kuskil pĂ€ringus CTE genereerimist ja kuskil plaanis sĂ”lme CTE, siis need sĂ”lmed on ĂŒksteisega kindlasti „kokku pĂ”rkuvad“, saame need kohe ĂŒhendada.

Ülesanne „tĂ€rniga“: CTE-d vĂ”ivad olla sissepoole pandud.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada
Need vÔivad olla vÀga halvasti sisse pandud, isegi sama nimetusega. NÀiteks vÔite sees CTE A teha CTE X, ja samal tasemel sees CTE B teha jÀlle CTE X:

WITH A AS (
  WITH X AS (...)
  SELECT ...
)
, B AS (
  WITH X AS (...)
  SELECT ...
)
...

Selle seondumise juures peate seda mĂ”istma. MĂ”ista seda „silmadega“ — isegi plaani nĂ€hes, isegi pĂ€ringu keha nĂ€hes — on vĂ€ga keeruline. Kui teie CTE genereerimine on keeruline, sissepandud, pĂ€ringud suured — siis on see tĂ€iesti alateadlik.

UNION

Kui meie pĂ€ringus on mĂ€rksĂ”na UNION [ALL] (ĂŒhendusoperaator kahe valimi vahel), siis on sellele plaanis kas sĂ”lm Lisa, vĂ”i mĂ”ni muu Rekursiivne Liit.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

See, mis on â€žĂŒleval“ meie sĂ”lmest, on esimene jĂ€rglane, mis on „all“ – teine. Kui meil on „kleeps“ mitmest plokist korraga, siis UNION -sĂ”lm on ikkagi vaid ĂŒks, aga lapsi on tal mitte kaks, vaid palju – jĂ€rjestikku, nagu nad kĂ€ivad: UNION (...) -- #1 UNION ALL (...) -- #2 UNION ALL (...) -- #3 LisaLisa -> ... #1 -> ... #2 -> ... #3

  : rekursiivse valiku genereerimise sees (

) vĂ”ib olla ka rohkem kui ĂŒks

Ülesanne „tĂ€rniga“. Kuid alati on rekursiivne ainult kĂ”ige viimane plokk pĂ€rast viimastWITH RECURSIVE. KĂ”ik, mis on ĂŒleval – see on ĂŒks, aga teine UNIONWITH RECURSIVE T AS( (...) -- #1 UNION ALL (...) -- #2, siin lĂ”ppeb rekursiooni algse seisundi genereerimine UNION ALL (...) -- #3, ainult see plokk on rekursiivne ja vĂ”ib sisaldada viitamist T-le ) ... UNIONSelliseid nĂ€iteid tuleb ka osata „Àra kleepida“. Siin nĂ€eme, et UNION:

-segmente oli meie pĂ€ringus 3 tĂŒkki. Vastavalt on ĂŒhel

-sĂ”lm, ja teisel – UNIONAndmete lugemise ja kirjutamise UNION vastab Lisa-узДл, а ĐŽŃ€ŃƒĐłĐŸĐŒŃƒ — Rekursiivne Liit.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Đ§Ń‚Đ”ĐœĐžĐ”-Đ·Đ°ĐżĐžŃŃŒ ĐŽĐ°ĐœĐœŃ‹Ń…

NĂŒĂŒd, kui oleme kĂ”ik selgeks teinud, teame, milline pĂ€ringu osa vastab millisele plaani osale. Nendes osades saame hĂ”lpsalt ja muretult leida need objektid, mis on "loetavad".

PĂ€ringu vaatenurgast ei tea me, kas see on tabel vĂ”i CTE, kuid need on esitatud sama sĂ”lme kaudu. Vahemikud. Plaani osas, mida "loetakse", on see samuti ĂŒsna piiratud sĂ”lmede kogum:

  • JĂ€rje skaneerimine laual [tbl]
  • Bitmap Heap Scan laual [tbl]
  • Indeksi [ainult] skaneeri [tagurpidi] kasutades [idx] [tbl] peal
  • CTE skane ĂŒhe peal [cte]
  • Sisestage/Uuendus/Delete laual [tbl]

Me teame plaani ja pĂ€ringu struktuuri, blokke vastavaid, objektide nimesid — teeme ĂŒhemĂ”ttelise vastenduse.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Taaskord ĂŒlesanne "tĂ€hega".VĂ”tame pĂ€ringu, teeme selle tĂ€itmise, meil ei ole ĂŒhtegi aliasit — me lugesime lihtsalt kaks korda sama CTE-st.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Vaata plaani — mis juhtus? Miks on meil alias? Me ei tellinud seda. Kust see "number" ilmus?

PostgreSQL lisab selle automaatselt. Peame lihtsalt mĂ”istma, et selline alias ei oma meie plaaniga vastendamiseks mingit tĂ€hendust, see on lihtsalt siia lisatud. Ärgem pöörakem sellele tĂ€helepanu.

Teine ĂŒlesanne "tĂ€hega".: kui loeme sektsioonitud tabelist, saame sĂ”lme Lisa vĂ”i Liitu Lisa, mis koosneb suurest hulgast "lastest", ja igaĂŒks neist on mingisugune Scantabeli-sektsioonist: JĂ€rje skaneerimine, Bitmap Heap Scan vĂ”i Index Scan. Kuid igal juhul on need "lapsed" lihtsad pĂ€ringud — neid sĂ”lmi saab eristada Lisa pĂ€ringus UNION.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Selliseid sĂ”lmi me samuti mĂ”istame, kogume â€žĂŒhte kuhja“ ja ĂŒtleme: "kĂ”ik, mida sa megatable'ist lugesid — see on siin ja allpool puu sees".

„Lihtsad“ andmete saamise sĂ”lmed

PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

VÀÀrtuste skannimine vastab plaanile VÄÄRTUSED pĂ€ringus.

Tulemus — see on pĂ€ring, kus puuduvad FROM nĂ€iteks SELECT 1. VĂ”i kui teil on ilmselgelt vale avaldis WHERE-plokis (siis tekib atribuut One-Time Filter):

EXPLAIN ANALYZE
SELECT * FROM pg_class WHERE FALSE; -- vÔi 0 = 1

Tulemus  (kulu=0.00..0.00 read=0 laius=230) (reaalne aeg=0.000..0.000 read=0 kordused=1)
  One-Time Filter: vale

Funktsionaalne skaneerimine „kaardistatakse“ vastava nimega SRF-idele.

Aga subpĂ€ringutega on kĂ”ik keerulisem — kahjuks ei pruugi need alati muutuda Alguspakett/Alamplaan. MĂ”nikord need muutuvad ... Join vĂ”i ... Anti Join, eriti kui kirjutate midagi sellist nagu WHERE NOT EXISTS .... Ja seal ei Ă”nnestuge alati kombineerida — plaani tekstis vastavad sĂ”lmed operatorite plaanile ei ole.

Taaskord ĂŒlesanne "tĂ€hega".: mitu VÄÄRTUSED pĂ€ringus. Sellisel juhul ja plaanis saate mitu sĂ”lme VÀÀrtuste skannimine.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Erilist tĂ€helepanu vÀÀrivad „numbrilised“ suffiksid — need lisatakse just vastava leidmise jĂ€rjekorras VÄÄRTUSED-plokkide kaudu pĂ€ringu kĂ€igus ĂŒlalt alla.

Andmete töötlemine

Nagu kĂ”ik on meie pĂ€ringus lahti seletatud — jÀÀb vaid Limit.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Siin on kĂ”ik lihtne — sellised sĂ”lmed nagu Limit, Sorteeri, Kogumine, WindowAgg, Unikaalne «kaardistatakse» ĂŒks-ĂŒhele vastavate operaatoritega pĂ€ringus, kui need seal on. Siin ei ole mingeid «tĂ€hti» ega keerukusi.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

JOIN

Keerukused tekivad, kui me tahame neid omavahel ĂŒhendada. JOIN Seda ei pruugi alati teha, kuid vĂ”imalik on.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

PĂ€ringu analĂŒsaatori seisukohalt on meil sĂ”lm LiituExpr, millel on tĂ€pselt kaks last — vasak ja parem. See on vastavalt see, mis on â€žĂŒle“ teie JOIN'i ja see, mis on „alla“ selle pĂ€ringus kirjutatud.

Ja plaani seisukohast on see mĂ”ne * TsĂŒkkel/* Liitu-sĂ”lme kaks jĂ€rglast. Nested Loop, Hash Anti Join,
 — midagi sellist.

Kasutame lihtsat loogikat: kui meil on tabelid A ja B, mis omavahel â€žĂŒhenduvad“ plaanis, siis vĂ”isid nad pĂ€ringus olla paigutatud kas A-JOIN-B, vĂ”i B-JOIN-A. Proovime neid nii ĂŒhendada, proovime neid vastupidi ĂŒhendada, ja nii seni, kuni sellised paarid otsa saavad.

VĂ”tame meie sĂŒntaktilise puu, vĂ”tame meie plaani, vaatame neid
 ei sarnane!
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Kujundame graafikutena ĂŒmber — oh, nĂŒĂŒd nĂ€eb see juba vĂ€lja nagu midagi!
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Kasutame tĂ€helepanekut, et meil on sĂ”lmed, millel on samaaegselt lapsed B ja C — meile ei ole oluline, millises jĂ€rjekorras. Ühendame need ja pöörame sĂ”lme pilti.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Vaadime uuesti. NĂŒĂŒd on meil sĂ”lmed koos lastega A ja paar (B + C) — need on ĂŒhilduvad.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

SuurepĂ€rane! Tundub, et meil on need kaks JOIN plaanisĂ”lme pĂ€ringust edukalt ĂŒhendatud.

Kahjuks ei lahenda see ĂŒlesanne alati.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

NĂ€iteks kui pĂ€ringus on A JOIN B JOIN C, ja plaanis on esmakordselt ĂŒhendatud „ÀÀrsed” sĂ”lmed A ja C. Ja kui pĂ€ringus sellist operaatorit pole, ei ole meil midagi rĂ”hutada, pole millele vihjatagi. Sama kehtib „komade” kohta, kui kirjutate A, B.

Kuid enamikul juhtudel Ă”nnestub peaaegu kĂ”ik sĂ”lmed „lahustada” ja saada selline ajaprofiil vasakul — tĂ€pselt nagu Google Chromes, kui analĂŒĂŒsite JavaScripti koodi. NĂ€ete, kui palju aega iga rida ja iga operaator „tĂ€ideti”.
PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Ja et teil oleks kÔigi nende funktsioonide kasutamine mugav, oleme loonud salvestamise arhivis, kus saate salvestada ja hiljem leida oma plaane koos seotud pÀringutega vÔi kellegagi jagada linki.

Kui aga peate lihtsalt tooma loetamatud pĂ€ringud mĂ”istlikku vormi, kasutage meie „normaliseerijat”.

PostgreSQL Query Profiler: kuidas plaani ja pÀringut vastandada

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster