PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Daugelis jau naudojasi paaiškinti.tensor.ru – mūsų PostgreSQL plano vizualizacijos paslauga gali nežinoti apie vieną iš savo supergalių – paversti sunkiai skaitomą serverio žurnalo dalį...

PostgreSQL Query Profiler: kaip suderinti planą ir užklausą
... į gražiai suplanuotą užklausą su atitinkamų plano mazgų kontekstinėmis užuominomis:

PostgreSQL Query Profiler: kaip suderinti planą ir užklausą
Šiame antrosios jo dalies nuoraše ataskaita PGConf.Russia 2020 m Aš jums papasakosiu, kaip mums tai pavyko.

Pirmosios dalies, skirtos tipinėms užklausų vykdymo problemoms ir jų sprendimams, stenogramą rasite straipsnyje „Receptai sergančioms SQL užklausoms“.



Pirma, pradėkime dažyti - ir plano nebespalvinsime, jau nuspalvinome, jau turime gražią ir suprantamą, bet prašymas.

Mums atrodė, kad su tokiu nesuformatuotu „lapu“ iš rąsto ištrauktas prašymas atrodo labai negražiai ir todėl nepatogu.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Ypač kai kūrėjai „suklijuoja“ užklausos turinį kode (tai, žinoma, yra antipatternas, bet taip atsitinka) vienoje eilutėje. Siaubinga!

Nupieškime tai kažkaip gražiau.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Ir jei galime tai gražiai nupiešti, tai yra, išardyti ir vėl sudėti užklausos turinį, tada prie kiekvieno šios užklausos objekto galime „prisegti“ užuominą - kas atsitiko atitinkamame plano taške.

Užklausos sintaksės medis

Norėdami tai padaryti, pirmiausia reikia išanalizuoti užklausą.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Nes mes turime sistemos branduolys veikia „NodeJS“., tada mes sukūrėme tam modulį, galite Raskite jį „GitHub“.. Tiesą sakant, tai yra išplėstinis „surišimas“ su paties PostgreSQL analizatoriaus vidiniais elementais. Tai reiškia, kad gramatika yra paprasčiausiai sudaryta dvejetainiu būdu ir surišama iš NodeJS. Kaip pagrindą ėmėme kitų žmonių modulius – didelės paslapties čia nėra.

Pateikiame užklausos turinį kaip įvestį į savo funkciją – išvestyje gauname išanalizuoti sintaksės medį JSON objekto pavidalu.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Dabar galime pereiti per šį medį priešinga kryptimi ir surinkti užklausą su norimomis įtraukomis, spalvomis ir formatavimu. Ne, tai nėra pritaikoma, bet mums atrodė, kad tai būtų patogu.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Atvaizdavimo užklausa ir plano mazgai

Dabar pažiūrėkime, kaip galime sujungti planą, kurį analizavome pirmame žingsnyje, ir užklausą, kurią analizavome antrajame žingsnyje.

Paimkime paprastą pavyzdį – turime užklausą, kuri generuoja CTE ir nuskaito iš jos du kartus. Jis sukuria tokį planą.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

CTE

Jei atidžiai žiūrėsite, iki 12 versijos (arba pradedant nuo jos raktiniu žodžiu MATERIALIZED) formavimas CTE yra absoliuti kliūtis planuotojui.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Tai reiškia, kad jei kur nors užklausoje matome CTE generavimą, o kažkur plane – mazgą CTE, tada šie mazgai neabejotinai „kariauja“ vienas su kitu, galime iš karto juos sujungti.

Problema su žvaigždute: CTE galima įdėti.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą
Yra labai prastai įdėtų ir net tokių pat pavadinimu. Pavyzdžiui, galite viduje CTE A padaryti CTE Xir viduje tame pačiame lygyje CTE B daryk tai dar kartą CTE X:

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

Lygindami turite tai suprasti. Suprasti tai „akimis“ – net pamatyti planą, net pamatyti prašymo turinį – labai sunku. Jei jūsų CTE karta yra sudėtinga, įdėta, o užklausos yra didelės, tai visiškai nesąmoninga.

SĄJUNGA

Jei užklausoje turime raktinį žodį UNION [ALL] (dviejų pavyzdžių sujungimo operatorius), tada plane jis atitinka bet kurį mazgą Append, arba kai kurie Recursive Union.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Tai, kas yra „aukščiau“. UNION - tai pirmasis mūsų mazgo palikuonis, kuris yra „žemiau“ - antrasis. Jei per UNION tada turime kelis blokelius „suklijuoti“ vienu metu Append-vis tiek bus tik vienas mazgas, bet jame bus ne du, o daug vaikų - tokia tvarka, kokia jie eina, atitinkamai:

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

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

Problema su žvaigždute: viduje rekursinio atrankos generavimas (WITH RECURSIVE) taip pat gali būti daugiau nei vienas UNION. Tačiau tik pats paskutinis blokas po paskutinio visada yra rekursyvus UNION. Viskas aukščiau yra viena, bet skirtinga UNION:

WITH RECURSIVE T AS(
  (...) -- #1
UNION ALL
  (...) -- #2, тут кончается генерация стартового состояния рекурсии
UNION ALL
  (...) -- #3, только этот блок рекурсивный и может содержать обращение к T
)
...

Taip pat reikia mokėti „išskirti“ tokius pavyzdžius. Šiame pavyzdyje tai matome UNION-Mūsų prašyme buvo 3 segmentai. Atitinkamai vienas UNION atitinka Append-mazgas, o į kitą - Recursive Union.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Skaityti-rašyti duomenis

Viskas išdėstyta, dabar žinome, kuri užklausos dalis atitinka kurią plano dalį. Ir šiuose kūriniuose galime lengvai ir natūraliai rasti tuos objektus, kurie yra „skaitomi“.

Žvelgiant iš užklausos, mes nežinome, ar tai lentelė, ar CTE, bet jas žymi tas pats mazgas RangeVar. Kalbant apie „skaitomumą“, tai taip pat yra gana ribotas mazgų rinkinys:

  • Seq Scan on [tbl]
  • Bitmap Heap Scan on [tbl]
  • Index [Only] Scan [Backward] using [idx] on [tbl]
  • CTE Scan on [cte]
  • Insert/Update/Delete on [tbl]

Žinome plano ir užklausos struktūrą, žinome blokų atitiktį, žinome objektų pavadinimus – atliekame palyginimą vienas su vienu.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Vėlgi užduotis "su žvaigždute". Mes priimame užklausą, vykdome ją, neturime jokių slapyvardžių – tiesiog du kartus perskaitome jį iš tos pačios CTE.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Žiūrime į planą – kokia problema? Kodėl mes turėjome slapyvardį? Mes jo neužsakėme. Iš kur jis gavo tokį „numerio numerį“?

PostgreSQL jį prideda pats. Jums tereikia tai suprasti tik toks slapyvardis pas mus palyginimo su planu tikslams tai neturi prasmės, čia tiesiog pridedama. Nekreipkime į jį dėmesio.

Antrasis užduotis "su žvaigždute": jei skaitome iš skaidytos lentelės, tada gausime mazgą Append arba Merge Append, kurį sudarys daugybė „vaikų“ ir kiekvienas iš jų bus kažkaip Scan'om iš lentelės skyriaus: Seq Scan, Bitmap Heap Scan arba Index Scan. Bet bet kokiu atveju šie „vaikai“ nebus sudėtingos užklausos - taip galima atskirti šiuos mazgus nuo Append prie UNION.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Mes taip pat suprantame tokius mazgus, surenkame juos „į vieną krūvą“ ir sakome: „viskas, ką skaitote iš megatable, yra čia ir po medžiu".

„Paprasti“ duomenų priėmimo mazgai

PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Values Scan atitinka planą VALUES prašyme.

Result yra prašymas be FROM patinka SELECT 1. Arba kai sąmoningai klaidingai išsireiškiate WHERE-blokas (tada pasirodo atributas One-Time Filter):

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

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

Function Scan „žemėlapį“ su to paties pavadinimo SRF.

Tačiau su įdėtomis užklausomis viskas yra sudėtingiau – deja, jos ne visada virsta InitPlan/SubPlan. Kartais jie virsta ... Join arba ... Anti Join, ypač kai rašai kažką panašaus WHERE NOT EXISTS .... Ir čia ne visada įmanoma juos sujungti – plano tekste nėra operatorių, atitinkančių plano mazgus.

Vėlgi užduotis "su žvaigždute": kai kurie VALUES prašyme. Tokiu atveju ir plane gausite kelis mazgus Values Scan.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

„Sunumeruotos“ priesagos padės jas atskirti viena nuo kitos - jos pridedamos tiksliai tokia tvarka, kokia yra atitinkamos. VALUES- blokuoja užklausą iš viršaus į apačią.

Duomenų apdorojimas

Atrodo, kad viskas mūsų prašyme buvo sutvarkyta – liko tik Limit.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Bet čia viskas paprasta - tokie mazgai kaip Limit, Sort, Aggregate, WindowAgg, Unique „susieti“ su atitinkamais užklausos operatoriais, jei jie yra. Čia nėra „žvaigždžių“ ar sunkumų.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

PRISIJUNK

Sunkumai iškyla, kai norime derinti JOIN tarp savęs. Tai ne visada įmanoma, bet įmanoma.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Užklausos analizatoriaus požiūriu, turime mazgą JoinExpr, kuri turi lygiai du vaikus – kairę ir dešinę. Atitinkamai tai yra „virš“ jūsų JOIN ir kas parašyta „po juo“ prašyme.

O plano požiūriu tai du kai kurių palikuonys * Loop/* Join- mazgas. Nested Loop, Hash Anti Join,... - kažkas panašaus.

Pasinaudokime paprasta logika: jei turime lenteles A ir B, kurios plane „susijungia“ viena su kita, tai užklausoje jos gali būti išdėstytos arba A-JOIN-BArba B-JOIN-A. Pabandykime derinti taip, bandykime derinti atvirkščiai ir taip toliau, kol pritrūks tokių porų.

Paimkime savo sintaksės medį, paimkime savo planą, pažiūrėkime į juos... nepanašūs!
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Perbraižykime grafikų pavidalu – oi, jau kažkas atrodo!
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Pastebėkime, kad turime mazgų, kurie vienu metu turi vaikus B ir C – mums nesvarbu, kokia tvarka. Sujunkite juos ir apverskime mazgo paveikslėlį.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Pažiūrėkime dar kartą. Dabar turime mazgus su vaikais A ir poromis (B + C) - su jais taip pat suderinami.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Puiku! Pasirodo, mes esame šie du JOIN iš užklausos su plano mazgai buvo sėkmingai sujungti.

Deja, ši problema ne visada išsprendžiama.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Pavyzdžiui, jei prašyme A JOIN B JOIN C, o plane pirmiausia buvo sujungti “išoriniai” mazgai A ir C. Bet tokio operatoriaus prašyme nėra, neturime ką paryškinti, prie ko prisegti užuominą. Tas pats yra su „kableliu“, kai rašai A, B.

Tačiau daugeliu atvejų beveik visi mazgai gali būti „atsieti“ ir jūs galite laiku gauti tokį profiliavimą kairėje pusėje – tiesiogine prasme, kaip „Google Chrome“, kai analizuojate „JavaScript“ kodą. Galite pamatyti, kiek laiko užtruko kiekviena eilutė ir kiekvienas teiginys, kol buvo „vykdyti“.
PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

O kad jums būtų patogiau visu tuo naudotis, padarėme saugyklą archyvas, kur galite išsaugoti ir vėliau rasti savo planus kartu su susijusiomis užklausomis arba bendrinti nuorodą su kuo nors.

Jei jums tiesiog reikia įvesti neįskaitomą užklausą į tinkamą formą, naudokite mūsų "normalizatorius".

PostgreSQL Query Profiler: kaip suderinti planą ir užklausą

Šaltinis: www.habr.com

Добавить комментарий