PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Mnogi, ki že uporabljajo expand.tensor.ru - naša storitev za vizualizacijo načrta PostgreSQL se morda ne zaveda ene od svojih velemoči - obračanje težko berljivega dela strežniškega dnevnika ...

PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo
... v lepo oblikovano poizvedbo s kontekstualnimi namigi za ustrezna vozlišča načrta:

PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo
V tem prepisu drugega dela nj poročilo na PGConf.Russia 2020 Povedal vam bom, kako nam je to uspelo.

Prepis prvega dela, posvečenega tipičnim težavam pri izvajanju poizvedb in njihovim rešitvam, najdete v članku "Recepti za slabe poizvedbe SQL".



Najprej začnimo z barvanjem - in ne bomo več barvali načrta, že smo ga pobarvali, že imamo lepo in razumljivo, ampak zahtevo.

Zdelo se nam je, da je zahteva, izvlečena iz dnevnika z neformatiranim "listom", videti zelo grda in zato neprijetna.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Še posebej, ko razvijalci "zlepijo" telo zahteve v kodi (to je seveda antipattern, vendar se zgodi) v eno vrstico. Grozno!

Narišimo to nekako lepše.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

In če lahko to lepo narišemo, torej razstavimo in ponovno sestavimo telo zahteve, potem lahko vsakemu predmetu te zahteve »pripnemo« namig - kaj se je zgodilo na ustrezni točki v načrtu.

Drevo sintakse poizvedbe

Za to je treba zahtevo najprej razčleniti.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Ker imamo jedro sistema deluje na NodeJS, potem smo naredili modul za to, lahko poiščite na GitHubu. Pravzaprav so to razširjene "povezave" na notranjost samega razčlenjevalnika PostgreSQL. To pomeni, da je slovnica preprosto binarno prevedena in nanjo so narejene vezi iz NodeJS. Za osnovo smo vzeli module drugih ljudi - tukaj ni velike skrivnosti.

Telo zahteve podajamo kot vhod v našo funkcijo - na izhodu dobimo razčlenjeno skladenjsko drevo v obliki objekta JSON.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Zdaj lahko gremo skozi to drevo v nasprotni smeri in sestavimo zahtevo z zamiki, barvami in oblikovanjem, ki jih želimo. Ne, tega ni mogoče prilagoditi, vendar se nam je zdelo, da bi bilo to priročno.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Preslikava vozlišč poizvedbe in načrta

Zdaj pa poglejmo, kako lahko združimo načrt, ki smo ga analizirali v prvem koraku, in poizvedbo, ki smo jo analizirali v drugem.

Vzemimo preprost primer – imamo poizvedbo, ki ustvari CTE in ga dvakrat prebere. On ustvari tak načrt.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

CTE

Če ga natančno pogledate, do različice 12 (ali začnete od nje s ključno besedo MATERIALIZED) nastanek CTE je absolutna ovira za načrtovalca.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

To pomeni, da če vidimo generacijo CTE nekje v zahtevi in ​​vozlišče nekje v načrtu CTE, potem se ta vozlišča med seboj zagotovo »bojijo«, jih lahko takoj združimo.

Težava z zvezdico: CTE je mogoče ugnezditi.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo
Obstajajo zelo slabo ugnezdeni in celo istoimenski. Na primer, lahko notri CTE A naredi CTE X, in na isti ravni znotraj CTE B naredi še enkrat CTE X:

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

Ko primerjate, morate to razumeti. Razumeti to "z očmi" - celo videti načrt, celo videti telo zahteve - je zelo težko. Če je vaša generacija CTE zapletena, ugnezdena in so zahteve velike, potem je popolnoma nezavedna.

UNION

Če imamo v poizvedbi ključno besedo UNION [ALL] (operator združevanja dveh vzorcev), potem v načrtu ustreza bodisi vozlišču Append, ali nekaj Recursive Union.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Tisto, kar je "zgoraj" zgoraj UNION - to je prvi potomec našega vozlišča, ki je "spodaj" - drugi. Če skozi UNION imamo več blokov "zlepljenih" naenkrat, torej Append- še vedno bo samo eno vozlišče, vendar ne bo imelo dveh, ampak veliko otrok - v vrstnem redu, v katerem gredo:

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

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

Težava z zvezdico: znotraj generiranja rekurzivnega vzorčenja (WITH RECURSIVE) je lahko tudi več kot ena UNION. Toda samo zadnji blok za zadnjim je vedno rekurziven UNION. Vse zgoraj je eno, a različno UNION:

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

Takšne primere moraš znati tudi »štrleti«. V tem primeru to vidimo UNION-v naši zahtevi so bili 3 segmenti. V skladu s tem ena UNION соответствующий Append-vozlišče, in na drugo - Recursive Union.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Podatki za branje in pisanje

Vse je postavljeno, zdaj vemo, kateri del zahteve ustreza kateremu delu načrta. In v teh delih zlahka in naravno najdemo tiste predmete, ki so »berljivi«.

Z vidika poizvedbe ne vemo, ali gre za tabelo ali CTE, vendar ju določa isto vozlišče RangeVar. Kar zadeva "berljivost", je to tudi dokaj omejen nabor vozlišč:

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

Poznamo strukturo načrta in poizvedbe, poznamo korespondenco blokov, poznamo imena objektov - naredimo primerjavo ena proti ena.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Ponovno naloga "z zvezdico". Sprejmemo zahtevo, jo izvedemo, nimamo vzdevkov - samo dvakrat jo preberemo iz istega CTE.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Pogledamo načrt - v čem je problem? Zakaj smo imeli vzdevek? Nismo ga naročili. Kje dobi takšno "številko"?

PostgreSQL ga doda sam. Samo to moraš razumeti samo tak vzdevek za nas za primerjavo z načrtom nima smisla, tukaj je preprosto dodana. Ne bodimo pozorni nanj.

2. naloga "z zvezdico": če beremo iz particionirane tabele, bomo dobili vozlišče Append ali Merge Append, ki bo sestavljen iz velikega števila »otrok«, od katerih bo vsak nekako Scan'om iz razdelka tabele: Seq Scan, Bitmap Heap Scan ali Index Scan. Toda v vsakem primeru ti "otroci" ne bodo zapletene poizvedbe - tako je mogoče ta vozlišča razlikovati od Append na UNION.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Razumemo tudi takšne vozle, jih zberemo "na en kup" in rečemo: "vse, kar preberete iz megatable, je tukaj in spodaj".

"Enostavna" vozlišča za sprejem podatkov

PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Values Scan ustreza načrtu VALUES v zahtevi.

Result je prošnja brez FROM nekako SELECT 1. Ali ko imate namenoma lažen izraz WHERE-block (takrat se pojavi atribut 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 »map« na istoimenske SRF-je.

Toda z ugnezdenimi poizvedbami je vse bolj zapleteno - na žalost se ne spremenijo vedno v InitPlan/SubPlan. Včasih se spremenijo v ... Join ali ... Anti Join, sploh ko napišeš kaj takega WHERE NOT EXISTS .... In tukaj jih ni vedno mogoče združiti - v besedilu načrta ni operaterjev, ki bi ustrezali vozliščem načrta.

Ponovno naloga "z zvezdico": nekaj VALUES v zahtevi. V tem primeru in v načrtu boste dobili več vozlišč Values Scan.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

"Oštevilčene" pripone jih bodo pomagale razlikovati med seboj - dodane so točno v vrstnem redu, v katerem so najdene ustrezne VALUES-bloki vzdolž zahteve od zgoraj navzdol.

Obdelava podatkov

Zdi se, da je bilo vse v naši zahtevi urejeno - vse, kar je ostalo, je Limit.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Ampak tukaj je vse preprosto - takšna vozlišča kot Limit, Sort, Aggregate, WindowAgg, Unique »preslikati« ena proti ena na ustrezne operaterje v zahtevi, če so tam. Tu ni "zvezd" ali težav.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

PRIDRUŽITE

Težave nastanejo, ko želimo kombinirati JOIN med sabo. To ni vedno mogoče, je pa mogoče.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Z vidika razčlenjevalnika poizvedb imamo vozlišče JoinExpr, ki ima točno dva otroka – levega in desnega. To je torej tisto, kar je "nad" vašim JOIN-om in tisto, kar je v zahtevi zapisano "pod" njim.

In z vidika načrta sta to dva potomca nekaterih * Loop/* Join-vozlišče. Nested Loop, Hash Anti Join,... - nekaj takega.

Uporabimo preprosto logiko: če imamo tabeli A in B, ki se v načrtu "združita", potem sta v zahtevi lahko locirani bodisi A-JOIN-BAli B-JOIN-A. Poskusimo kombinirati tako, poskusimo kombinirati obratno in tako naprej, dokler nam takih parov ne zmanjka.

Vzemimo naše skladenjsko drevo, vzemimo naš načrt, poglejmo jih ... niso podobni!
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Prerišimo ga v obliki grafov - oh, že nekaj izgleda!
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Upoštevajmo, da imamo vozlišča, ki imajo istočasno otroka B in C – vseeno nam je, v kakšnem vrstnem redu. Združimo jih in obrnemo sliko vozlišča.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Poglejmo še enkrat. Zdaj imamo vozlišča z otroki A in pari (B + C) – združljiva tudi z njimi.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Super! Izkazalo se je, da sva ta dva JOIN iz zahteve z vozlišči načrta so bila uspešno združena.

Žal, ta problem ni vedno rešen.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Na primer, če v zahtevi A JOIN B JOIN C, in v načrtu sta bila najprej povezana »zunanja« vozlišča A in C. Toda v zahtevi ni takega operaterja, nimamo česa poudariti, česa priložiti namig. Enako je z "vejico", ko pišeš A, B.

Toda v večini primerov je skoraj vsa vozlišča mogoče "odvezati" in pravočasno pridobiti tovrstno profiliranje na levi - dobesedno, kot v Google Chromu, ko analizirate kodo JavaScript. Vidite lahko, koliko časa je trajalo, da se je »izvedla« vsaka vrstica in vsak stavek.
PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

In da bi vam olajšali uporabo vsega tega, smo naredili shrambo arhiv, kjer lahko shranite in pozneje najdete svoje načrte skupaj s povezanimi zahtevami ali delite povezavo z nekom.

Če morate samo neberljivo poizvedbo spraviti v ustrezno obliko, uporabite naš "normalizator".

PostgreSQL Query Profiler: kako uskladiti načrt in poizvedbo

Vir: www.habr.com

Dodaj komentar