PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Mange som allerede bruker explain.tensor.ru — vĂ„r tjeneste for visualisering av PostgreSQL-planer, er kanskje ikke klar over en av dens superkrefter — Ă„ gjĂžre en vanskelig lesbar del av serverloggen til...

PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

en pent formatert forespþrsel med kontekstuelle tips om de relevante nodene i planen:

PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen
I denne tolkningen av den andre delen av sin presentasjon pÄ PGConf.Russia 2020 vil jeg fortelle hvordan vi klarte Ä gjÞre dette.

Med transkriptet til den fÞrste delen, som omhandler typiske ytelsesproblemer med forespÞrslene og deres lÞsninger, kan man finne i artikkelen «Oppskrifter for syke SQL-forespÞrslene».


Spill video

FĂžrst skal vi ta oss av fargeleggingen — og vi skal ikke fargelegge planen, den har vi allerede fargelagt, den er allerede pen og forstĂ„elig, men forespĂžrselen.

Vi syntes at en slik uformaterte «klut» hentet fra loggen sĂ„ veldig stygg ut og derfor — uhĂ„ndterlig.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Spesielt nÄr utviklerne i koden «limer» forespÞrselens kropp (dette er selvfÞlgelig en antipattern, men det skjer) i én linje. Grusomt!

La oss tegne dette pÄ en mer vakker mÄte.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Og hvis vi kan tegne det pent, det vil si dele og sette sammen forespĂžrselens kropp igjen, sĂ„ kan vi ogsĂ„ «koble» tips til hvert objekt i denne forespĂžrselen — hva som skjedde pĂ„ det relevante punktet i planen.

Syntaktisk tre av forespĂžrselen

For Ä gjÞre dette, mÄ forespÞrselen fÞrst deles opp.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Siden vĂ„r systemkjernen kjĂžrer pĂ„ NodeJS, laget vi et lite modul til den, dere kan finne det pĂ„ GitHub. Faktisk er dette utvidede «bindinger» til innsiden av selve PostgreSQL-parseren. Det vil si, bare binĂŠrt kompilert grammatikk og bindinger laget pĂ„ NodeJS-siden. Vi tok utgangspunkt i andres moduler — det er ingen stor hemmelighet her.

Vi mater inn forespĂžrselens kropp i funksjonen vĂ„r — pĂ„ utgangen fĂ„r vi et syntaktisk tre delt opp i form av et JSON-objekt.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

NÄ kan vi gÄ tilbake gjennom dette treet og samle forespÞrselen med de innrykkene, fargingen og formateringen vi Þnsker. Nei, dette er ikke konfigurerbart, men vi syntes det ville vÊre praktisk slik.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

SammenfĂžring av noder i forespĂžrselen og planen

NĂ„ skal vi se hvordan vi kan kombinere planen, som vi delte opp i fĂžrste trinn, og forespĂžrselen, som vi delte opp i det andre.

La oss ta et enkelt eksempel — vi har en spþrring som danner en CTE og leser fra den to ganger. Den genererer en slik plan.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

CTE

Hvis vi ser nÞye pÄ det, er det frem til versjon 12 (eller fra den med nÞkkelordet MATERIALIZED) dannelse CTE er en ubestridelig barriere for planleggeren..
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

SÄ hvis vi ser en generasjon av CTE et sted i spÞrringen og et sted i planen en node, CTE, sÄ slÄr disse nodene tydeligvis sammen, vi kan umiddelbart kombinere dem.

Oppgaven med en stjerne: CTE-er kan vĂŠre innkapslet.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen
De kan vÊre veldig dÄrlig innkapslet, og til og med med samme navn. For eksempel kan du inne i CTE A lage CTE X, og pÄ samme nivÄ inne i CTE B gjÞre igjen CTE X:

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

NĂ„r du sammenligner, mĂ„ du forstĂ„ dette. Å forstĂ„ det med «Þyne» — selv ved Ă„ se planen, selv ved Ă„ se kroppen av spĂžrringen — er veldig vanskelig. Hvis genereringen av CTE er komplisert, innkapslet, og spĂžrringene er store — sĂ„ er det i det hele tatt ubevisst.

Den definerende spÞrringen mÄ ha

Hvis vi har nÞkkelordet i spÞrringen UNION [ALL] (operator for Ä bli med to utvalg), sÄ svarer det i planen til enten en node Legg til, eller en eller annen Rekursiv Union.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Det som er «ovenfor» noden vĂ„r, er den fĂžrste avkommet, mens det som er «nedenfor» — er den andre. Hvis det er «limt» flere blokker sammen, sĂ„ Den definerende spĂžrringen mĂ„ ha -noden vil fortsatt vĂŠre bare Ă©n, men den har mange barn — i rekkefĂžlge som de kommer, henholdsvis: Den definerende spĂžrringen mĂ„ ha (...) -- #1 UNION ALL (...) -- #2 UNION ALL (...) -- #3 Legg tilVedlegg -> ... #1 -> ... #2 -> ... #3

  : inne i genereringen av rekursiv utvelgelse (

) kan det ogsÄ vÊre mer enn én

Oppgaven med en stjerne. Men alltid er det bare den siste blokken etter den sisteWITH RECURSIVE. Alt som er over — er Ă©n, men en annen Den definerende spĂžrringen mĂ„ haWITH RECURSIVE T AS( (...) -- #1 UNION ALL (...) -- #2, her slutter genereringen av starttilstanden for rekursivitet UNION ALL (...) -- #3, bare denne blokken er rekursiv og kan referere til T ) ... Den definerende spĂžrringen mĂ„ haSĂ„nne eksempler mĂ„ vi ogsĂ„ kunne «lime». Her ser vi at Den definerende spĂžrringen mĂ„ ha:

-segmentene i vÄr spÞrring var 3 stykker. FÞlgelig, en til

-node, og en annen — Den definerende spĂžrringen mĂ„ haLesing-skriving av data. Den definerende spĂžrringen mĂ„ ha tilsvarer Legg tilDa, la oss si, nĂ„ vet vi hvilken del av forespĂžrselen som tilsvarer hvilken del av planen. Og i disse delene kan vi enkelt og uanstrengt finne de objektene som «leses». Rekursiv Union.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Fra spþrringens synspunkt vet vi ikke — er det en tabell eller CTE, men de betegnes med samme node.

ВсД, Ń€Đ°Đ·Đ»ĐŸĐ¶ĐžĐ»Đž, Ń‚Đ”ĐżĐ”Ń€ŃŒ ĐŒŃ‹ Đ·ĐœĐ°Đ”ĐŒ, ĐșаĐșĐŸĐč ĐșŃƒŃĐŸŃ‡Đ”Đș Đ·Đ°ĐżŃ€ĐŸŃĐ° ĐșаĐșĐŸĐŒŃƒ ĐșŃƒŃĐŸŃ‡Đșу ĐżĐ»Đ°ĐœĐ° ŃĐŸĐŸŃ‚ĐČДтстĐČŃƒĐ”Ń‚. И ĐČ ŃŃ‚ĐžŃ… ĐșŃƒŃĐŸŃ‡Đșах ĐŒŃ‹ ĐŒĐŸĐ¶Đ”ĐŒ лДгĐșĐŸ Đž ĐœĐ”ĐżŃ€ĐžĐœŃƒĐ¶ĐŽĐ”ĐœĐœĐŸ ĐœĐ°Đčто тД ĐŸĐ±ŃŠĐ”Đșты, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Â«Ń‡ĐžŃ‚Đ°ŃŽŃ‚ŃŃÂ».

ĐĄ Ń‚ĐŸŃ‡ĐșĐž Đ·Ń€Đ”ĐœĐžŃ Đ·Đ°ĐżŃ€ĐŸŃĐ° ĐŒŃ‹ ĐœĐ” Đ·ĐœĐ°Đ”ĐŒ — таблОца ŃŃ‚ĐŸ ОлО CTE, ĐœĐŸ ĐŸĐ±ĐŸĐ·ĐœĐ°Ń‡Đ°ŃŽŃ‚ŃŃ ĐŸĐœĐž ĐŸĐŽĐžĐœĐ°ĐșĐŸĐČŃ‹ĐŒ ŃƒĐ·Đ»ĐŸĐŒ RangeVar. Og i planen «leses» - dette er ogsĂ„ et ganske begrenset sett med noder:

  • Sekvensskanning pĂ„ [tbl]
  • Bitmap Haugskanning pĂ„ [tbl]
  • Indeks [Only] Skann [Bakover] bruker [idx] pĂ„ [tbl]
  • CTE-skanning pĂ„ [cte]
  • Sett inn/Update/Delete pĂ„ [tbl]

Vi kjenner strukturen til planen og forespÞrselen, vi vet hvordan blokkene samsvarer, vi kjenner objektenes navn - sÄ vi gjÞr en entydig tilknytning.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Igjen oppgaven med «stjernene». Vi tar forespÞrselen, utfÞrer den, og vi har ingen aliaser - vi har bare lest fra én CTE to ganger.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Vi ser pÄ planen - hva er problemet? Hvorfor har vi fÄtt opp et alias? Vi ba ikke om det. Hvor kommer denne «nummerte» fra?

PostgreSQL legger det til selv. Man mÄ bare forstÄ at akkurat det aliaset for vÄre formÄl i tilknytningen til planen ikke har noen mening, det er bare lagt til her. Vi vil ikke ta hensyn til det.

Andre oppgaven med «stjernene»: hvis vi leser fra en partisjonert tabell, sÄ fÄr vi en node Legg til eller SlÄ sammen og legg til, som vil bestÄ av et stort antall «barn», og hver av dem vil vÊre en slags Scanfra tabell-partisjonen: Sekvensi Skanning, Bitmap Haugskanning eller Index Scan. Men uansett vil disse «barnene» ikke vÊre kompliserte forespÞrselene - sÄ disse nodene kan skilles fra Legg til nÄr Den definerende spÞrringen mÄ ha.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

SÄdanne noder forstÄr vi ogsÄ, samler «i en haug» og sier: "alt du leste fra megatable - dette er her og nedover i treet".

«Enkle» noder for datainnhenting

PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Verdi Scan i planen tilsvarer Spesifisere i forespĂžrselen.

Result - dette er en forespÞrsel uten FROM slik SELECT 1. Eller nÄr du har en bevisst falsk uttrykk i Den maksimale/minimale hurtig oppfriskningen etter sletting eller blandede DML har ikke samme oppfÞrsel som innsetting-tilfelle. Den sletter og beregner pÄ nytt maks/min verdiene for de berÞrte gruppene. Du mÄ vÊre oppmerksom pÄ dens ytelseseffekt.-blokken (da oppstÄr attributten One-Time Filter):

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

Resultat  (kostnad=0.00..0.00 rader=0 bredde=230) (faktisk tid=0.000..0.000 rader=0 lĂžkker=1)
  One-Time Filter: falsk

Funksjon Skanning «kartlegges» til de tilsvarende SRF-ene.

Men med innviklede forespÞrsler er det vanskeligere - dessverre endres de ikke alltid til InitPlan/SubPlan. Noen ganger endres de til ... Join eller ... Anti Join, spesielt nÄr du skriver noe som WHERE NOT EXISTS .... Og der er det ikke alltid mulig Ä kombinere - i tekstplanen finnes det ingen tilsvarende noder for operatÞrene.

Igjen oppgaven med «stjernene»: flere Spesifisere i forespÞrselen. I slike tilfeller vil du i planen ogsÄ fÄ flere noder Verdi Scan.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Å skille dem fra hverandre kan hjelper «nummerte» suffikser - de legges til nĂžyaktig i rekkefĂžlgen som de tilsvarende Spesifisere-blokkene finnes i forespĂžrselen ovenfra og ned.

Databehandling

Det ser ut som vi har gÄtt gjennom alle i forespÞrselen vÄr - det er bare igjen Grense.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Men her er alt enkelt - slike noder som Begrens, Sorter, Aggregere, WindowAgg, Unik «kartlegges» en-til-en pÄ de tilsvarende operatÞrene i forespÞrselen, hvis de finnes. Her er det ingen «stjerner» og vanskeligheter.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

JOIN

Vanskeligheter oppstÄr nÄr vi Þnsker Ä kombinere JOIN med hverandre. Dette er ikke alltid mulig, men det kan gjÞres.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Fra parserens synspunkt har vi en node Bli med, som har nĂžyaktig to etterkommere – venstre og hĂžyre. Dette er henholdsvis det som er "over" din JOIN og det som er "under" det som stĂ„r i forespĂžrselen.

Og sett fra planens synspunkt har dette to etterkommere av en eller annen * Lþkka/* Bli med-node. Nested Loop, Hash Anti Join,
 – noe slik.

La oss bruke enkel logikk: hvis vi har tabeller A og B som "joiner" seg med hverandre i planen, kan de i forespÞrselen vÊre plassert enten A-JOIN-B, eller B-JOIN-A. La oss prÞve Ä kombinere dem pÄ denne mÄten, sÄ prÞver vi det motsatte, og sÄ videre til slike par tar slutt.

La oss ta vÄrt syntaktiske tre, ta planen vÄr, se pÄ dem... det ser ikke likt ut!
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Vi tegner det pĂ„ nytt som grafer – Ă„h, nĂ„ begynner det Ă„ ligne noe!
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

La oss merke oss at vi har noder som samtidig har barn B og C – vi bryr oss ikke om rekkefþlgen. La oss kombinere dem og snu bildet av noden.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

La oss ta en ny titt. NĂ„ har vi noder med barn A og paret (B + C) – la oss kombinere dem ogsĂ„.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Flott! Det viser seg at vi pÄ en vellykket mÄte har kombinert disse to JOIN fra forespÞrselen med nodene i planen.

Dessverre lĂžses ikke denne oppgaven alltid.
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

For eksempel, hvis i forespÞrselen A JOIN B JOIN C, men i planen har de "ytterste" nodene A og C blitt kombinert fÞrst. Og i forespÞrselen finnes det ingen slik operator, sÄ vi har ingen Ä fremheve, ingen Ä knytte hintet til. Det samme gjelder "komma" nÄr du skriver A, B.

Men i de fleste tilfeller klarer vi Ă„ "lĂžse opp" nesten alle noder og fĂ„ denne profiling til venstre etter tid – bokstavelig talt, som i Google Chrome nĂ„r du analyserer JavaScript-koden. Du ser hvor lang tid hver linje og hver operatĂžr "ble utfĂžrt".
PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Og for at dere skal kunne bruke alt dette mer behagelig, har vi laget lagring arkiv, hvor du kan lagre og deretter finne planene dine sammen med de tilknyttede forespĂžrslene eller dele en lenke med noen.

Hvis du bare mÄ bringe en ulest forespÞrsel i en forstÄelig form, bruk gjerne vÄr "normalisator".

PostgreSQL Query Profiler: hvordan matche planen og forespĂžrselen

Kilde: habr.com

KjĂžp pĂ„litelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere đŸ”„ KjĂžp pĂ„litelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere | ProHoster