PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Paljud, kes juba kasutavad selgitus.tensor.ru - meie PostgreSQL-i plaani visualiseerimisteenus ei pruugi olla teadlik ühest oma ülivõimest - serverilogi raskesti loetava osa keeramisest...

PostgreSQL Query Profiler: kuidas sobitada plaan ja päring
... kaunilt kujundatud päringusse koos kontekstuaalsete vihjetega vastavate plaanisõlmede jaoks:

PostgreSQL Query Profiler: kuidas sobitada plaan ja päring
Selles tema teise osa ärakirjas aruanne PGConf.Russia 2020. aastal Ma räägin teile, kuidas meil see õnnestus.

Esimese osa ärakirja, mis on pühendatud tüüpilistele päringu jõudlusprobleemidele ja nende lahendustele, leiate artiklist "Retseptid haigete SQL-päringute jaoks".



Esiteks alustame värvimist - ja me ei värvi enam plaani, oleme selle juba värvinud, meil on see juba ilus ja arusaadav, aga palve.

Meile tundus, et sellise vormindamata “lehega” näeb palgist tõmmatud taotlus väga kole ja seetõttu ebamugav.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Eriti kui arendajad "liimivad" päringu keha koodi (see on muidugi antimuster, kuid see juhtub) ühele reale. Õudne!

Joonistame selle kuidagi ilusamini.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Ja kui suudame selle ilusti joonistada, st päringu korpuse lahti võtta ja uuesti kokku panna, siis saame igale selle päringu objektile “kinnitada” vihje - mis juhtus plaani vastavas punktis.

Päringu süntaksipuu

Selleks tuleb taotlus esmalt sõeluda.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Sest meil on süsteemi tuum töötab NodeJS-is, siis tegime selle jaoks mooduli, saate leidke see GitHubist. Tegelikult on need laiendatud "sidemed" PostgreSQL-i parseri enda sisemistele. See tähendab, et grammatika kompileeritakse lihtsalt binaarselt ja sellele tehakse NodeJS-ist sidumised. Võtsime aluseks teiste inimeste moodulid – siin pole suurt saladust.

Toidame päringu keha oma funktsiooni sisendiks - väljundis saame parsitud süntaksipuu JSON-objekti kujul.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Nüüd saame selle puu läbida vastupidises suunas ja koostada päringu koos soovitud taande, värvide ja vorminguga. Ei, seda ei saa kohandada, kuid meile tundus, et see oleks mugav.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Päringu ja plaani sõlmede kaardistamine

Nüüd vaatame, kuidas saame ühendada esimeses etapis analüüsitud plaani ja teises etapis analüüsitud päringu.

Võtame lihtsa näite – meil on päring, mis genereerib CTE ja loeb sellest kaks korda. Ta koostab sellise plaani.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

CTE

Kui vaatate seda hoolikalt, siis kuni versioonini 12 (või alustades sellest märksõnaga MATERIALIZED) moodustamine CTE on planeerija jaoks absoluutne takistus.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

See tähendab, et kui me näeme kuskil taotluses CTE generatsiooni ja kuskil plaanis sõlme CTE, siis need sõlmed kindlasti omavahel “kaklevad”, saame need kohe kombineerida.

Probleem tärniga: CTE-sid saab pesastada.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring
On väga halvasti pesastatud ja isegi samanimelisi. Näiteks saate sees CTE A teha CTE X, ja sees samal tasemel CTE B tee seda uuesti CTE X:

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

Võrreldes peate sellest aru saama. Selle "silmaga" mõistmine - isegi plaani nägemine, isegi taotluse sisu nägemine - on väga raske. Kui teie CTE põlvkond on keeruline, pesastatud ja taotlused on suured, on see täiesti teadvuseta.

LIIDU

Kui meil on päringus märksõna UNION [ALL] (kahe valimi ühendamise operaator), siis plaanis vastab see kummalegi sõlmele Append, või mõni Recursive Union.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

See, mis on ülal "üleval". UNION - see on meie sõlme esimene järeltulija, mis on "all" - teine. Kui läbi UNION meil on siis mitu plokki korraga “liimitud”. Append-sõlm on endiselt ainult üks, kuid sellel pole mitte kaks, vaid palju lapsi - vastavalt nende liikumise järjekorras:

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

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

Probleem tärniga: rekursiivse valimi genereerimise sees (WITH RECURSIVE) võib olla ka rohkem kui üks UNION. Kuid ainult kõige viimane plokk pärast viimast on alati rekursiivne UNION. Kõik ülaltoodud on üks, kuid erinev UNION:

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

Samuti peate suutma selliseid näiteid "välja tõmmata". Selles näites näeme seda UNION-meie taotluses oli 3 segmenti. Vastavalt sellele üks UNION vastab Append-sõlm ja teisele - Recursive Union.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Andmete lugemine-kirjutamine

Kõik on paika pandud, nüüd teame, milline osa taotlusest vastab millisele plaaniosale. Ja nendest tükkidest leiame lihtsalt ja loomulikult need objektid, mis on "loetavad".

Päringu seisukohast ei tea me, kas see on tabel või CTE, kuid neid määrab sama sõlm RangeVar. Ja "loetavuse" osas on see ka üsna piiratud sõlmede komplekt:

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

Teame plaani ja päringu ülesehitust, teame plokkide vastavust, teame objektide nimetusi - teeme üks-ühele võrdluse.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Jällegi ülesanne "tärniga". Võtame päringu vastu, täidame selle, meil pole varjunimesid – lugesime seda lihtsalt kaks korda samast CTE-st.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Vaatame plaani – milles probleem? Miks meil oli alias? Me ei tellinud seda. Kust ta sellise “numbrinumbri” võtab?

PostgreSQL lisab selle ise. Peate lihtsalt sellest aru saama just selline alias meie jaoks planeeringuga võrdlemise eesmärgil pole sellel mingit mõtet, see on lihtsalt siia lisatud. Ärme pööra talle tähelepanu.

Teine ülesanne "tärniga": kui loeme partitsioonidega tabelist, siis saame sõlme Append või Merge Append, mis koosneb suurest hulgast “lastest” ja millest igaüks on kuidagi Scan'om tabeliosast: Seq Scan, Bitmap Heap Scan või Index Scan. Kuid igal juhul ei saa need "lapsed" olla keerulised päringud - nii saab neid sõlmi eristada Append juures UNION.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Mõistame ka selliseid sõlmi, kogume need "ühte hunnikusse" ja ütleme: "kõik, mida megatable’ist loed, on siin ja puu otsas".

"Lihtsad" andmete vastuvõtmise sõlmed

PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Values Scan vastab plaanile VALUES taotluses.

Result on taotlus ilma FROM meeldib SELECT 1. Või kui teil on tahtlikult vale väljend WHERE-block (siis ilmub atribuut 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 "kaart" samanimelistele SRF-idele.

Kuid pesastatud päringutega on kõik keerulisem - kahjuks ei muutu need alati nendeks InitPlan/SubPlan. Mõnikord muutuvad need ... Join või ... Anti Join, eriti kui kirjutad midagi taolist WHERE NOT EXISTS .... Ja siin pole neid alati võimalik kombineerida - plaani tekstis pole plaani sõlmedele vastavaid operaatoreid.

Jällegi ülesanne "tärniga": mõned VALUES taotluses. Sel juhul ja plaanis saate mitu sõlme Values Scan.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

"Nummerdatud" järelliited aitavad neid üksteisest eristada - need lisatakse täpselt vastavate leidmise järjekorras VALUES-blokeerib päringu ülalt alla.

Andmetöötlus

Näib, et kõik meie taotluses on lahendatud – jäänud on vaid Limit.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Kuid siin on kõik lihtne - sellised sõlmed nagu Limit, Sort, Aggregate, WindowAgg, Unique "kaardistada" üks-ühele päringu vastavate operaatoritega, kui need on olemas. Siin pole "tähti" ega raskusi.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

LIITU

Raskused tekivad siis, kui tahame kombineerida JOIN omavahel. See ei ole alati võimalik, kuid see on võimalik.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Päringu parseri seisukohast on meil sõlm JoinExpr, millel on täpselt kaks last – vasak ja parem. See on seega see, mis on teie JOIN-i kohal ja mis on taotluses kirjutatud selle alla.

Ja planeeringu seisukohalt on tegemist mõne kahe järeltulijaga * Loop/* Join-sõlm. Nested Loop, Hash Anti Join,... - midagi sellist.

Kasutame lihtsat loogikat: kui meil on tabelid A ja B, mis plaanis teineteisega “liituvad”, siis päringus võiksid need paikneda kas A-JOIN-BVõi B-JOIN-A. Proovime kombineerida nii, proovime kombineerida teistpidi ja nii edasi, kuni sellised paarid saavad otsa.

Võtame oma süntaksipuu, võtame oma plaani, vaatame neid... mitte sarnased!
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Joonistame selle graafikute kujul ümber – oi, see tundub juba midagi!
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Pangem tähele, et meil on sõlmed, millel on samaaegselt lapsed B ja C – meid ei huvita, mis järjekorras. Kombineerime need ja pöörame sõlme pildi ümber.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Vaatame uuesti. Nüüd on meil sõlmed lastega A ja paaridega (B + C) - ühilduvad ka nendega.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Suurepärane! Selgub, et me oleme need kaks JOIN taotlusest koos plaani sõlmedega ühendati edukalt.

Kahjuks seda probleemi alati ei lahendata.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Näiteks kui taotluses A JOIN B JOIN C, ja plaanis ühendati ennekõike “välimised” sõlmed A ja C. Aga sellist operaatorit taotluses pole, meil pole midagi esile tõsta, millele vihjet lisada. Sama on "komaga" kirjutamisel A, B.

Kuid enamikul juhtudel saab peaaegu kõik sõlmed "lahti siduda" ja sellist profiili saate õigeaegselt vasakule teha - sõna otseses mõttes nagu Google Chrome'is JavaScripti koodi analüüsimisel. Näete, kui kaua kulus iga rea ​​ja iga avalduse täitmiseks.
PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Ja selleks, et teil oleks seda kõike mugavam kasutada, oleme teinud hoiustamise arhiiv, kus saate salvestada ja hiljem leida oma plaanid koos seotud taotlustega või jagada linki kellegagi.

Kui teil on vaja lihtsalt loetamatu päring viia sobivasse vormi, kasutage meie "normalisaator".

PostgreSQL Query Profiler: kuidas sobitada plaan ja päring

Allikas: www.habr.com

Lisa kommentaar