PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

Paljud, kes juba kasutavad explain.tensor.ru — meie PostgreSQL plaanide visualiseerimise teenust, ei pruugi olla teadlikud ĂŒhe tema supervĂ”ime — keerulisest serveri logi kĂ€rpimise teisendamisest


PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

 ilusaks vormindatud pÀringuks kontekstuaalsete vihjetega vastavate plaanide sÔlmedele:

PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut
Selles oma raportis PGConf.Russia 2020 rÀÀgin, kuidas me selle saavutame.

Esimese osa transkriptsiga, mis kĂ€sitleb tĂŒĂŒpilisi pĂ€ringute jĂ”udluse probleeme ja nende lahendusi, saate tutvuda artiklis „Retseptid haigestuvatele SQL-pĂ€ringutele“.


MĂ€ngi videot

Alustame vĂ€rvimisest — ja vĂ€rvime juba mitte plaani, see on meil juba ilus ja arusaadav, vaid pĂ€ringut.

Meie arvates nĂ€eb eksportimisse logidest vĂ€lja nĂ€htud „riides“ vĂ€ga kole vĂ€lja ja seega — ebamugav.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

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

Kujundame selle kuidagi ilusamaks.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

Ja kui suudame selle ilusateks kujundada, siis saame pĂ€ringu iga objekti jaoks „paigaldada“ vihje — mis toimus vastavas plaanipunktis.

PĂ€ringu sĂŒntaktiline puu

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

Kuna meil sĂŒsteemi tuum töötab NodeJS-il, siis tegime sellele mooduli, vĂ”ite leida selle GitHubist. Tegelikult on see laiemad „sidumised“ PostgreSQL-i parseri sisestruktuuride suhtes. See tĂ€hendab lihtsalt binaarselt koostatud grammatika ning sellele on tehtud sidumised NodeJS-i poolt. Kasutasime teiste moodulite baasi — siin pole suurt saladust.

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

NĂŒĂŒd saab seda puu kaudu tagurpidi joosta ja koguda pĂ€ringu, mida soovime, mingite sisetĂ”mmete, vĂ€rvimise, vormindamisega. Ei, see ei ole konfigureeritav, kuid tundus, et just nii oleks mugav.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

PÀringu ja plaani sÔlmede vastavus

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

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

CTE

Kui sellele tÀhelepanelikult vaadata, siis kuni 12. versioonini (vÔi alates sellest, koos reaalsÔnaga MATERIALIZED) on loomine CTE absoluutne barjÀÀr planeerijale.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

Seega, kui me nĂ€eme kusagil pĂ€ringus CTE genereerimist ja kusagil plaanis sĂ”lme CTE, siis need sĂ”lmed on kindlasti omavahel seotud, me saame need kohe ĂŒhendada.

Ülesanne 'tĂ€rniga': CTE-d vĂ”ivad olla pesakohtadest.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut
Need vÔivad olla vÀga halvasti pesakohtades, ja isegi sama nimega. NÀiteks vÔite sees CTE A moodi 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 ...
)
...

Seda kokkupanekut peab mĂ”istma. MĂ”ista seda 'silmadega' — isegi nĂ€hes plaani, isegi nĂ€hes pĂ€ringu keha — on vĂ€ga raske. Kui teil on CTE genereerimine keeruline, pesakohtades, pĂ€ringud suured — siis pole see ĂŒldse aru saanud.

kogumi operaator, toetavad

Kui meie pĂ€ringus on reaalsĂ”na UNION [ALL] (kahe valiku ĂŒhendamise operaator), vastab sellele plaanis kas sĂ”lm Lisa, vĂ”i mĂ”ni Rekursiivne Liit.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

See, mis 'ĂŒles' on meie sĂ”lme esimene laps, ja mis 'alla' — teine. Kui lĂ€bi kogumi operaator, toetavad meil on 'liimitud' mitu plokki korraga, siis kogumi operaator, toetavad -sĂ”lm on ainult ĂŒks, aga 'lapsesid' on tal mitte kaks, vaid palju — vastavalt nende jĂ€rjekonnale: Lisa(...) -- #1 UNION ALL (...) -- #2 UNION ALL (...) -- #3

  Append
  -> ... #1
  -> ... #2
  -> ... #3

: sees genereerimise rekurssi valik (

Ülesanne 'tĂ€rniga') vĂ”ib samuti olla rohkem kui ĂŒks.WITH RECURSIVEKuid alati on rekursiivne ainult viimane plokk pĂ€rast viimast. kogumi operaator, toetavadKĂ”ik, mis ĂŒleval — see on ĂŒks, aga teine kogumi operaator, toetavadWITH RECURSIVE T AS( (...) -- #1 UNION ALL (...) -- #2, siin lĂ”peb rekursside algseisundi genereerimine UNION ALL (...) -- #3, ainult see plokk on rekursiivne ja vĂ”ib sisaldada viite T-le ) ... kogumi operaator, toetavad:

Taolisi nÀiteid peab samuti oskama 'lahutada'. Siin nÀeme, et

-segmente meie pĂ€ringus oli 3 tĂŒkki. Seega, ĂŒhele kogumi operaator, toetavad-sĂ”lm, ja teisele — kogumi operaator, toetavad vastab LisaAndmete lugemine ja kirjutamine Rekursiivne Liit.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

Nii, me oleme lahti harutanud, nĂŒĂŒd teame, milline tĂŒkk pĂ€ringust vastab millisele tĂŒkk plaanist. Ja nendes tĂŒkkides saame lihtsalt ja vaevata leida need objektid, mida 'loetakse'.

PĂ€ringu seisukohalt me ei tea — kas see on tabel vĂ”i CTE, kuid need tĂ€histatakse sama sĂ”lmega.

KĂŒsimuse vaatepunktist ei tea me, kas see on tabel vĂ”i CTE, kuid neid tĂ€histatakse sama sĂ”lmega. RangeVar. Ja plaan „loetav” - see on samuti ĂŒsna piiratud sĂ”lmede kogum:

  • JĂ€rjesta skaneerimine [tbl] peal
  • Bitmapi virna skaneerimine [tbl] peal
  • Indeks [Ainult] Skaneeri [Tagasi] kasutades [idx] [tbl] peal
  • CTE skannimine [cte] peal
  • Sisestage/Uuenda/Kustutage [tbl] peal

Me teame plaani ja pĂ€ringu struktuuri, teame plokkide vastavust, teame objektide nimesid - teeme ĂŒhemĂ”ttelise vastavuse.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

JĂ€llegi ĂŒlesanne „tĂ€hega”. VĂ”tame pĂ€ringu, tĂ€idame selle, meil ei ole mingeid alias'e - me lihtsalt lugesime kaks korda ĂŒhest CTE-st.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

Vaata plaani - mis hĂ€da? Miks meil alias ilmus? Me ei tellinud seda. Kust see „numbriline” tuli?

PostgreSQL lisab selle ise. Tuleb lihtsalt mĂ”ista, et selline alias ei oma meile plaaniga vastavuse eesmĂ€rkide jaoks mingit mĂ”tet, see on lihtsalt siin lisatud. Ärge pöörake sellele tĂ€helepanu.

Teine ĂŒlesanne „tĂ€hega”: kui meil on lugemine jaotatud tabelist, saame sĂ”lme Lisa vĂ”i Ühenda Lisa, mis koosneb suurest hulgast „lastest”, kellest igaĂŒks on mingi Scan'ist jaotustabelist: JĂ€rjesta skaneerimine, Bitmapi virna skaneerimine vĂ”i Index Scan. Kuid igal juhul ei ole need „lapsed” keerulised pĂ€ringud - neid sĂ”lmi saab eristada Lisa kui kogumi operaator, toetavad.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

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

„Lihtsad” andmete saamise sĂ”lmed

PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

VÀÀrtuste skaneerimine plaanis vastab VÄÄRTUSTE pĂ€ringus.

Tulemus — see on pĂ€ring ilma KUST nĂ€iliselt SELECT 1. VĂ”i kui sul on vale vĂ€ljend KUS-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) (tode 0.000..0.000 read=0 tsĂŒklid=1)
  One-Time Filter: false

Funktsiooni skanner „maapuvad” vastava SRF-ga.

Kuid sisemiste pÀringutega on kÔik keerulisem - kahjuks ei muutu need alati Algusplaan/Alusplaan. MÔnikord muutuvad nad ... Join vÔi ... Anti Join, eriti kui sa kirjutad midagi sarnast WHERE NOT EXISTS .... Ja seal kombineerimine ei pruugi alati Ônnestuda - plaani tekstis vastavaid sÔlme operaatoritest pole.

JĂ€llegi ĂŒlesanne „tĂ€hega”: mitu VÄÄRTUSTE pĂ€ringus. Sellisel juhul saad plaanis mitu sĂ”lme VÀÀrtuste skaneerimine.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

Eristada neid ĂŒksteisest aitavad „numbrilised” suffiksid - need lisatakse tĂ€pselt plokkide vastavuse leidmise jĂ€rjekorras pĂ€ringu ĂŒlemisest osast alla. VÄÄRTUSTEAndmete töötlemine

Tundub, et oleme oma pÀringus kÔik lahti seletanud - alles on jÀÀnud vaid

Aga siin on kÔik lihtne - sellised sÔlmed nagu Piirang.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

„maapuvad” ĂŒks-ĂŒhele vastavatele operaatoritele pĂ€ringus, kui need seal olemas on. Siin ei ole mingeid „tĂ€hekesed” ega raskusi. Piirang, Sorteeri, Kogumine, WindowAgg, Ainulaadne Raskused tekivad, kui me tahame ĂŒhendada
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

JOIN

oma vahel. Seda ei ole alati lihtne teha, kuid vÔimalik. JOIN oma vahel. Seda ei ole alati vÔimalik teha, kuid see on vÔimalik.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

KĂŒsimuse parsija seisukohalt on meil sĂ”lm, LiituExpr, millel on tĂ€pselt kaks last — vasak ja parem. See on vastavalt see, mis on teie JOIN â€œĂŒleval” ja see, mis on “all” kirjutatud.

Ja plaani seisukohalt on see kaks last mĂ”nest * TsĂŒkkel/* Liitu-sĂ”lmest. SisekĂ€ik, Hash Anti Join,
 — midagi sellist.

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

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

Joonistame uuesti graafide vormis — o, nĂŒĂŒd hakkab midagi sarnast olema!
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

Pöörame tĂ€helepanu, et meil on sĂ”lmed, millel on samal ajal lapsed B ja C — meile ei ole oluline, mis jĂ€rjekorras. Ühendame need ja pöörame sĂ”lme pildi ĂŒmber.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

Vaadakem veel kord. NĂŒĂŒd on meil sĂ”lmed lastega A ja paar (B + C) — ĂŒhendame ka need.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

Fantastiline! Tundub, et me oleme need kaks JOIN pÀringust plaani sÔlmedega edukalt kokku viinud.

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

NĂ€iteks, kui pĂ€ringus on A JOIN B JOIN C, aga plaanis liitusid alguses â€œĂ€Ă€rmuslikud” sĂ”lmed A ja C. Ja pĂ€ringus pole sellist operaatorit, meil pole midagi, mida esile tĂ”sta, pole millele vihjet siduda. Sama kehtib “komma” kohta, kui kirjutate A, B.

Kuid enamikul juhtudel Ă”nnestub peaaegu kĂ”ik sĂ”lmed “lahti siduda” ja saada midagi sellist profiileerimise jĂ€rgi vasakul ajale — literally, nagu Google Chrome'is, kui analĂŒĂŒsite JavaScripti koodi. NĂ€ete, kui palju aega iga rida ja iga operaator “tĂ€ideti”.
PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

Ja et teil oleks kÔigega mugavam kasutada, oleme loonud salvestuse arhiiv, kus saate salvestada ja hiljem leida oma plaane koos seotud pÀringutega vÔi kellegagi jagada linki.

Kui teil on lihtsalt vaja tuua lugematu pĂ€ring korrektsetesse vormidesse, kasutage meie “normaliseerijat”.

PostgreSQL Query Profiler: kuidas kaardistada plaani ja pÀringut

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster