PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Margir sem eru nú þegar að nota útskýra.tensor.ru - PostgreSQL áætlun sjónræn þjónusta okkar gæti ekki verið meðvituð um einn af ofurkraftum sínum - að breyta erfiðu stykki af netþjónsskránni...

PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn
... í fallega hannaða fyrirspurn með samhengisvísum fyrir samsvarandi áætlunarhnúta:

PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn
Í þessu afriti af öðrum hluta hans skýrsla á PGConf.Russia 2020 Ég skal segja þér hvernig okkur tókst að gera þetta.

Afrit fyrsta hlutans, tileinkað dæmigerðum frammistöðuvandamálum fyrirspurna og lausnum þeirra, er að finna í greininni „Uppskriftir að veikum SQL fyrirspurnum“.



Fyrst skulum við byrja að lita - og við munum ekki lengur lita áætlunina, við höfum þegar litað það, við höfum það nú þegar fallegt og skiljanlegt, heldur beiðni.

Okkur virtist sem með svo ósniðnu „blaði“ lítur beiðnin sem var dregin úr stokknum mjög ljót út og því óþægileg.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Sérstaklega þegar forritarar „líma“ meginmál beiðninnar í kóðanum (þetta er auðvitað andmynstur, en það gerist) í einni línu. Hræðilegt!

Við skulum teikna þetta einhvern veginn fallegra.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Og ef við getum teiknað þetta fallega, það er að taka í sundur og setja saman meginmál beiðninnar, þá getum við "hengt" vísbendingu við hvern hlut þessarar beiðni - hvað gerðist á samsvarandi stað í áætluninni.

Fyrirspurnarsetningafræðitré

Til að gera þetta þarf fyrst að flokka beiðnina.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Vegna þess að við höfum kjarni kerfisins keyrir á NodeJS, þá gerðum við einingu fyrir það, þú getur finndu það á GitHub. Reyndar eru þetta útvíkkaðar „bindingar“ við innri hluti PostgreSQL þáttarans sjálfs. Það er að segja að málfræðin er einfaldlega tvöföld samansett og bindingar eru gerðar við hana frá NodeJS. Við tókum einingar annarra sem grunn - hér er ekkert stórt leyndarmál.

Við gefum meginmál beiðninnar sem inntak í aðgerðina okkar - við úttakið fáum við þáttað setningafræðitré í formi JSON hlutar.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Nú getum við keyrt í gegnum þetta tré í gagnstæða átt og sett saman beiðni með inndrætti, litun og sniði sem við viljum. Nei, þetta er ekki hægt að sérsníða, en okkur fannst þetta vera þægilegt.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Kortlagning fyrirspurna og áætlunarhnúta

Nú skulum við sjá hvernig við getum sameinað áætlunina sem við greindum í fyrsta skrefi og fyrirspurninni sem við greindum í því síðara.

Tökum einfalt dæmi - við erum með fyrirspurn sem býr til CTE og les úr henni tvisvar. Hann býr til slíka áætlun.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

CTE

Ef þú skoðar það vandlega, upp í útgáfu 12 (eða byrjar á því með lykilorðinu MATERIALIZED) myndun CTE er algjör hindrun fyrir skipuleggjanda.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Þetta þýðir að ef við sjáum CTE kynslóð einhvers staðar í beiðninni og hnút einhvers staðar í áætluninni CTE, þá „berjast“ þessir hnútar örugglega hver við annan, við getum samstundis sameinað þá.

Vandamál með stjörnu: Hægt er að hreiðra CTE.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn
Það eru mjög illa hreiður og jafnvel samnefndir. Til dæmis getur þú inni CTE A gera CTE X, og á sama stigi inni CTE B gerðu það aftur CTE X:

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

Þegar þú berð saman verður þú að skilja þetta. Að skilja þetta „með augum“ - jafnvel að sjá áætlunina, jafnvel sjá meginmál beiðninnar - er mjög erfitt. Ef CTE kynslóðin þín er flókin, hreiður og beiðnirnar eru stórar, þá er hún algjörlega meðvitundarlaus.

UNION

Ef við höfum leitarorð í fyrirspurninni UNION [ALL] (rekstraraðili að sameina tvö sýni), þá samsvarar það í áætluninni annað hvort hnút Append, eða eitthvað Recursive Union.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Það sem er „fyrir ofan“ UNION - þetta er fyrsti afkomandi hnútsins okkar, sem er "fyrir neðan" - sá seinni. Ef í gegnum UNION þá erum við með nokkra kubba „líma“ í einu Append-það verður samt aðeins einn hnút, en hann mun ekki hafa tvö, heldur mörg börn - í þeirri röð sem þau fara, í sömu röð:

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

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

Vandamál með stjörnu: innan endurkvæmrar sýnatöku (WITH RECURSIVE) geta líka verið fleiri en einn UNION. En aðeins síðasta blokkin á eftir þeim síðasta er alltaf endurkvæm UNION. Allt að ofan er eitt, en öðruvísi UNION:

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

Þú þarft líka að geta "stungið út" svona dæmi. Í þessu dæmi sjáum við það UNION-það voru 3 hlutir í beiðni okkar. Samkvæmt því, einn UNION соответствует Append-hnút, og til hins - Recursive Union.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Lesa-skrifa gögn

Allt er útbúið, nú vitum við hvaða hluti af beiðninni samsvarar hvaða hluta áætlunarinnar. Og í þessum verkum getum við auðveldlega og náttúrulega fundið þá hluti sem eru „læsilegir“.

Frá sjónarhóli fyrirspurnar vitum við ekki hvort það er tafla eða CTE, en þeir eru tilnefndir af sama hnút RangeVar. Og hvað varðar „læsileika“ er þetta líka frekar takmarkað sett af hnútum:

  • 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]

Við þekkjum uppbyggingu áætlunarinnar og fyrirspurnarinnar, við þekkjum samsvörun blokkanna, við vitum nöfnin á hlutunum - við gerum einn-á-mann samanburð.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Aftur verkefni "með stjörnu". Við tökum beiðnina, framkvæmum hana, við höfum engin samheiti - við lesum hana bara tvisvar frá sama CTE.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Við skoðum áætlunina - hvað er vandamálið? Af hverju vorum við með samnefni? Við pöntuðum það ekki. Hvar fær hann svona „númeranúmer“?

PostgreSQL bætir því við sjálft. Þú þarft bara að skilja það bara svona alias fyrir okkur, í þeim tilgangi að bera saman við áætlunina, þá meikar það ekkert vit, það er einfaldlega bætt við hér. Við skulum ekki veita honum athygli.

Annað verkefni "með stjörnu": ef við erum að lesa úr skipta töflu, þá fáum við hnút Append eða Merge Append, sem mun samanstanda af miklum fjölda „barna“ og hvert um sig verður einhvern veginn Scan'om úr töfluhlutanum: Seq Scan, Bitmap Heap Scan eða Index Scan. En í öllum tilvikum munu þessi „börn“ ekki vera flóknar fyrirspurnir - svona má greina þessa hnúta frá Append á UNION.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Við skiljum líka slíka hnúta, söfnum þeim „í einni bunka“ og segjum: „allt sem þú lest af megatable er hér og niður í trénu".

„Einfaldir“ gagnamóttökuhnútar

PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Values Scan samsvarar í áætlun VALUES í beiðninni.

Result er beiðni án FROM eins og SELECT 1. Eða þegar þú ert með vísvitandi ranga tjáningu í WHERE-blokk (þá birtist eigindin 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 „kort“ á samnefnda SRF.

En með hreiðri fyrirspurnum er allt flóknara - því miður breytast þær ekki alltaf í InitPlan/SubPlan. Stundum breytast þeir í ... Join eða ... Anti Join, sérstaklega þegar þú skrifar eitthvað eins og WHERE NOT EXISTS .... Og hér er ekki alltaf hægt að sameina þau - í texta áætlunarinnar eru engir rekstraraðilar sem samsvara hnútum áætlunarinnar.

Aftur verkefni "með stjörnu": sumir VALUES í beiðninni. Í þessu tilviki og í áætluninni færðu nokkra hnúta Values Scan.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

„Númeruð“ viðskeyti munu hjálpa til við að aðgreina þau hvert frá öðru - þeim er bætt við nákvæmlega í þeirri röð sem samsvarandi finnast VALUES-blokkir meðfram beiðninni frá toppi til botns.

Gagnavinnsla

Það virðist sem allt í beiðni okkar hafi verið raðað út - allt sem er eftir er Limit.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

En hér er allt einfalt - svo hnútar eins og Limit, Sort, Aggregate, WindowAgg, Unique „korta“ einn á einn til samsvarandi rekstraraðila í beiðninni, ef þeir eru til staðar. Það eru engar „stjörnur“ eða erfiðleikar hér.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

JOIN

Erfiðleikar koma upp þegar við viljum sameina JOIN sín á milli. Þetta er ekki alltaf hægt, en það er hægt.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Frá sjónarhóli fyrirspurnarþáttarans höfum við hnút JoinExpr, sem á nákvæmlega tvö börn - vinstri og hægri. Þetta er því það sem er „fyrir ofan“ JOIN þinn og það sem er skrifað „fyrir neðan“ í beiðninni.

Og frá sjónarhóli áætlunarinnar eru þetta tveir afkomendur sumra * Loop/* Join-hnút. Nested Loop, Hash Anti Join,... - eitthvað svoleiðis.

Við skulum nota einfalda rökfræði: ef við höfum töflur A og B sem „tengjast saman“ í áætluninni, þá gætu þær verið staðsettar annað hvort í beiðninni A-JOIN-BEða B-JOIN-A. Reynum að sameina svona, reynum að sameina á hinn veginn og svo framvegis þar til við erum uppiskroppa með svona pör.

Tökum setningafræðitréð okkar, tökum áætlunina okkar, skoðum þau... ekki svipað!
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Við skulum endurteikna það í formi línurita - ó, það lítur nú þegar út eins og eitthvað!
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Við skulum athuga að við höfum hnúta sem hafa börn B og C samtímis - okkur er alveg sama í hvaða röð. Við skulum sameina þau og snúa myndinni af hnútnum við.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Við skulum líta aftur. Nú erum við með hnúta með börnum A og pör (B + C) - samhæft þeim líka.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Frábært! Það kemur í ljós að við erum þessi tvö JOIN frá beiðninni og áætlunarhnútunum tókst að sameina.

Því miður er þetta vandamál ekki alltaf leyst.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Til dæmis, ef í beiðni A JOIN B JOIN C, og í áætluninni voru í fyrsta lagi tengdir „ytri“ hnútarnir A og C. En það er enginn slíkur rekstraraðili í beiðninni, við höfum ekkert að draga fram, ekkert að hengja vísbendingu við. Það er eins með "kommuna" þegar þú skrifar A, B.

En í flestum tilfellum er hægt að „losa“ næstum alla hnúta og þú getur fengið þessa tegund af sniði til vinstri í tíma - bókstaflega, eins og í Google Chrome þegar þú greinir JavaScript kóða. Þú getur séð hversu langan tíma það tók hverja línu og hverja yfirlýsingu að „framkvæma“.
PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Og til að gera það þægilegra fyrir þig að nota allt þetta höfum við búið til geymslu skjalasafn, þar sem þú getur vistað og síðar fundið áætlanir þínar ásamt tilheyrandi beiðnum eða deilt hlekknum með einhverjum.

Ef þú þarft bara að koma ólæsilegri fyrirspurn í fullnægjandi form skaltu nota „normalizer“ okkar.

PostgreSQL Query Profiler: hvernig á að passa saman áætlun og fyrirspurn

Heimild: www.habr.com

Bæta við athugasemd