PostgreSQL Query Profiler: ako spárovať plán a dopyt

Mnohí, ktorí už používajú vysvetliť.tensor.ru - naša služba vizualizácie plánu PostgreSQL si možno neuvedomuje jednu zo svojich superschopností - otáčanie ťažko čitateľného kusu denníka servera...

PostgreSQL Query Profiler: ako spárovať plán a dopyt
... do krásne navrhnutého dotazu s kontextovými radami pre zodpovedajúce uzly plánu:

PostgreSQL Query Profiler: ako spárovať plán a dopyt
V tomto prepise druhej časti jeho správa na PGConf.Russia 2020 Poviem vám, ako sa nám to podarilo.

Prepis prvej časti venovanej typickým problémom s výkonom dotazov a ich riešeniam nájdete v článku "Recepty na choré SQL dotazy".



Najprv začneme vyfarbovať - ​​a plán už nebudeme vyfarbovať, už sme ho vyfarbili, už ho máme krásny a zrozumiteľný, ale prosba.

Zdalo sa nám, že pri takomto nenaformátovanom „hárku“ vyzerá požiadavka vytiahnutá z guľatiny veľmi škaredo a teda nepohodlne.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Najmä, keď vývojári „nalepia“ telo požiadavky v kóde (toto je, samozrejme, antivzor, ​​ale stáva sa to) do jedného riadku. Strašné!

Poďme si to nakresliť nejako krajšie.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

A ak to dokážeme krásne nakresliť, teda rozobrať a poskladať telo požiadavky, potom môžeme ku každému predmetu tejto požiadavky „pripojiť“ nápovedu – čo sa stalo v príslušnom bode plánu.

Strom syntaxe dotazu

Aby ste to dosiahli, musíte žiadosť najskôr analyzovať.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Pretože máme jadro systému beží na NodeJS, potom sme na to urobili modul, môžete nájdete ho na GitHub. V skutočnosti ide o rozšírené „väzby“ na vnútorné časti samotného analyzátora PostgreSQL. To znamená, že gramatika je jednoducho binárne skompilovaná a väzby sú k nej vytvorené z NodeJS. Ako základ sme vzali moduly iných ľudí - tu nie je žiadne veľké tajomstvo.

Telo požiadavky podávame ako vstup do našej funkcie – na výstupe dostaneme syntaktický strom syntaxe vo forme objektu JSON.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Teraz môžeme prejsť cez tento strom v opačnom smere a zostaviť požiadavku s požadovanými odrážkami, farbením a formátovaním. Nie, toto nie je prispôsobiteľné, ale zdalo sa nám, že by to bolo pohodlné.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Mapovací dotaz a uzly plánu

Teraz sa pozrime, ako môžeme skombinovať plán, ktorý sme analyzovali v prvom kroku, a dotaz, ktorý sme analyzovali v druhom kroku.

Uveďme si jednoduchý príklad – máme dotaz, ktorý generuje CTE a číta z neho dvakrát. Vytvorí takýto plán.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

CTE

Ak sa na to pozriete pozorne, až po verziu 12 (alebo od nej od kľúčového slova MATERIALIZED) tvorenie CTE je pre plánovača absolútnou bariérou.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

To znamená, že ak niekde v požiadavke vidíme generovanie CTE a niekde v pláne uzol CTE, potom tieto uzly medzi sebou určite „bojujú“, môžeme ich hneď kombinovať.

Problém s hviezdičkou: CTE môžu byť vnorené.
PostgreSQL Query Profiler: ako spárovať plán a dopyt
Existujú veľmi zle vnorené a dokonca aj tie s rovnakým názvom. Môžete napríklad dovnútra CTE A robiť CTE Xa na rovnakej úrovni vo vnútri CTE B sprav to znovu CTE X:

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

Pri porovnávaní to musíte pochopiť. Pochopiť to „vašimi očami“ - dokonca vidieť plán, dokonca vidieť telo žiadosti - je veľmi ťažké. Ak je vaša generácia CTE zložitá, vnorená a požiadavky sú veľké, potom je to úplne v bezvedomí.

UNION

Ak máme v dopyte kľúčové slovo UNION [ALL] (operátor spojenia dvoch vzoriek), potom v pláne zodpovedá buď uzlu Append, alebo nejaké Recursive Union.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

To, čo je „hore“ vyššie UNION - toto je prvý potomok nášho uzla, ktorý je „dole“ - druhý. Ak cez UNION máme teda „zlepených“ niekoľko blokov naraz Append-stále bude len jeden uzol, ale nebude mať dve, ale veľa detí - v poradí, v akom idú, resp.

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

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

Problém s hviezdičkou: v rámci generovania rekurzívneho vzorkovania (WITH RECURSIVE) môže byť aj viac ako jeden UNION. Ale len úplne posledný blok po poslednom je vždy rekurzívny UNION. Všetko vyššie je jedno, ale iné UNION:

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

Musíte byť tiež schopní „vypichnúť“ takéto príklady. V tomto príklade to vidíme UNION-v našej žiadosti boli 3 segmenty. Podľa toho jeden UNION zodpovedá Append-uzol a do druhého - Recursive Union.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Čítanie a zápis údajov

Všetko je rozložené, teraz vieme, ktorá časť požiadavky zodpovedá ktorej časti plánu. A v týchto kúskoch ľahko a prirodzene nájdeme tie predmety, ktoré sú „čitateľné“.

Z hľadiska dopytu nevieme, či ide o tabuľku alebo CTE, ale sú označené rovnakým uzlom RangeVar. A pokiaľ ide o „čitateľnosť“, ide tiež o pomerne obmedzený súbor uzlov:

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

Poznáme štruktúru plánu a dopytu, poznáme korešpondenciu blokov, poznáme názvy objektov – robíme porovnanie jedna ku jednej.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Opäť úloha "s hviezdičkou". Prijmeme požiadavku, vykonáme ju, nemáme žiadne aliasy – len ju čítame dvakrát z toho istého CTE.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Pozeráme sa na plán – v čom je problém? Prečo sme mali alias? Neobjednali sme si to. Odkiaľ berie také „číslo“?

PostgreSQL to pridáva sám. Len to musíš pochopiť len taký alias pre nas to na ucely porovnania s planom nedava ziadny zmysel, jednoducho sa to tu pridava. Nevšímajme si ho.

Druhý úloha "s hviezdičkou": ak čítame z rozdelenej tabuľky, dostaneme uzol Append alebo Merge Append, ktorý bude pozostávať z veľkého počtu „detí“ a každé z nich bude nejako Scan'om zo sekcie tabuľky: Seq Scan, Bitmap Heap Scan alebo Index Scan. V každom prípade však tieto „deti“ nebudú zložité otázky - takto možno tieto uzly odlíšiť od Append na UNION.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Rozumieme aj takýmto uzlom, zbierame ich „na jednu hromadu“ a hovoríme: „všetko, čo čítate z megatable, je tu a dole na strome".

"Jednoduché" uzly na príjem údajov

PostgreSQL Query Profiler: ako spárovať plán a dopyt

Values Scan zodpovedá plánom VALUES v žiadosti.

Result je žiadosť bez FROM niečo ako SELECT 1. Alebo keď máte úmyselne falošný výraz WHERE-block (potom sa objaví atribút 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 „mapa“ k SRF s rovnakým názvom.

Ale s vnorenými dopytmi je všetko komplikovanejšie - bohužiaľ, nie vždy sa zmenia na InitPlan/SubPlan. Niekedy sa zmenia na ... Join alebo ... Anti Join, hlavne keď napíšeš niečo ako WHERE NOT EXISTS .... A tu nie je vždy možné ich kombinovať - ​​v texte plánu nie sú žiadne operátory zodpovedajúce uzlom plánu.

Opäť úloha "s hviezdičkou": niektoré VALUES v žiadosti. V tomto prípade a v pláne získate niekoľko uzlov Values Scan.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

„Očíslované“ prípony ich pomôžu odlíšiť od seba - pridávajú sa presne v poradí, v akom sa nachádzajú zodpovedajúce VALUES-blokuje pozdĺž požiadavky zhora nadol.

Spracovanie dát

Zdá sa, že všetko v našej žiadosti bolo vyriešené – zostáva už len všetko Limit.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Ale tu je všetko jednoduché - také uzly ako Limit, Sort, Aggregate, WindowAgg, Unique „mapovať“ jeden k jednému na zodpovedajúcich operátorov v požiadavke, ak tam sú. Nie sú tu žiadne „hviezdy“ ani ťažkosti.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

REGISTRÁCIA

Ťažkosti vznikajú, keď chceme kombinovať JOIN medzi sebou. Nie je to vždy možné, ale je to možné.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Z pohľadu analyzátora dotazov máme uzol JoinExpr, ktorá má rovno dve deti – ľavé a pravé. Toto je teda to, čo je „nad“ vaším JOINom a čo je napísané „pod“ v žiadosti.

A z pohľadu plánu ide o dvoch potomkov niektorých * Loop/* Join- uzol. Nested Loop, Hash Anti Join,... - niečo také.

Použime jednoduchú logiku: ak máme tabuľky A a B, ktoré sa navzájom „spájajú“ v pláne, potom v požiadavke môžu byť umiestnené buď A-JOIN-BAlebo B-JOIN-A. Skúsme kombinovať takto, skúsme kombinovať naopak a tak ďalej, kým sa nám takéto páry neminú.

Vezmime si náš syntaktický strom, zoberme si náš plán, pozrime sa na ne... nie podobné!
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Prekreslíme si to do podoby grafov – ach, už to na niečo vyzerá!
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Všimnime si, že máme uzly, ktoré majú súčasne deti B a C - je nám jedno, v akom poradí. Skombinujme ich a obrátime obrázok uzla.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Pozrime sa znova. Teraz máme uzly s deťmi A a pármi (B + C) - kompatibilné aj s nimi.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Skvelé! Ukazuje sa, že my dvaja sme JOIN z požiadavky s plánom uzly boli úspešne spojené.

Bohužiaľ, tento problém nie je vždy vyriešený.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

Napríklad, ak v žiadosti A JOIN B JOIN C, a v pláne boli v prvom rade spojené „vonkajšie“ uzly A a C. Ale v požiadavke takýto operátor nie je, nemáme čo zvýrazniť, k čomu pripojiť nápovedu. Rovnako je to aj s „čiarkou“, keď píšete A, B.

Vo väčšine prípadov však možno takmer všetky uzly „rozviazať“ a tento druh profilovania môžete získať na ľavej strane včas – doslova ako v prehliadači Google Chrome, keď analyzujete kód JavaScript. Môžete vidieť, ako dlho trvalo „vykonanie“ každého riadku a každého príkazu.
PostgreSQL Query Profiler: ako spárovať plán a dopyt

A aby bolo pre vás používanie tohto všetkého pohodlnejšie, vytvorili sme úložisko archív, kde si môžete uložiť a neskôr nájsť svoje plány spolu s priradenými požiadavkami alebo s niekým zdieľať odkaz.

Ak potrebujete len preniesť nečitateľný dotaz do adekvátnej podoby, použite náš "normalizátor".

PostgreSQL Query Profiler: ako spárovať plán a dopyt

Zdroj: hab.com

Pridať komentár