PostgreSQL Query Profiler: hvordan matche plan og spørring

Mange som allerede bruker explain.tensor.ru - Visualiseringstjenesten vår for PostgreSQL-plan er kanskje ikke klar over en av superkreftene sine - snu en vanskelig å lese del av serverloggen...

PostgreSQL Query Profiler: hvordan matche plan og spørring
... til en vakkert designet spørring med kontekstuelle hint for de tilsvarende plannodene:

PostgreSQL Query Profiler: hvordan matche plan og spørring
I denne utskriften av andre del av hans rapport på PGConf.Russia 2020 Jeg skal fortelle deg hvordan vi klarte å gjøre dette.

Transkripsjonen av den første delen, dedikert til typiske spørringsytelsesproblemer og deres løsninger, finnes i artikkelen "Oppskrifter for skrantende SQL-spørringer".



Først, la oss begynne å fargelegge - og vi vil ikke lenger fargelegge planen, vi har allerede farget den, vi har allerede den vakker og forståelig, men en forespørsel.

Det virket for oss at med et slikt uformatert "ark" ser forespørselen fra loggen veldig stygg ut og derfor upraktisk.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Spesielt når utviklere "limer" selve forespørselen i koden (dette er selvfølgelig et antimønster, men det skjer) på én linje. Fryktelig!

La oss tegne dette på en eller annen måte vakrere.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Og hvis vi kan tegne dette vakkert, det vil si demontere og sette sammen hoveddelen av forespørselen, så kan vi "feste" et hint til hvert objekt av denne forespørselen - hva som skjedde på det tilsvarende punktet i planen.

Spørringssyntakstre

For å gjøre dette må forespørselen først analyseres.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Fordi vi har kjernen i systemet kjører på NodeJS, så har vi laget en modul for det, du kan finn den på GitHub. Faktisk er disse utvidede "bindinger" til det indre av selve PostgreSQL-parseren. Det vil si at grammatikken ganske enkelt er binær kompilert og bindinger til den fra NodeJS. Vi tok andres moduler til grunn – det er ingen stor hemmelighet her.

Vi mater brødteksten til forespørselen som input til funksjonen vår - ved utgangen får vi et parset syntakstre i form av et JSON-objekt.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Nå kan vi kjøre gjennom dette treet i motsatt retning og sette sammen en forespørsel med innrykk, fargelegging og formatering vi ønsker. Nei, dette kan ikke tilpasses, men det virket for oss at dette ville være praktisk.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Kartlegging av spørrings- og plannoder

La oss nå se hvordan vi kan kombinere planen som vi analyserte i det første trinnet og spørringen som vi analyserte i det andre.

La oss ta et enkelt eksempel - vi har en spørring som genererer en CTE og leser fra den to ganger. Han lager en slik plan.
PostgreSQL Query Profiler: hvordan matche plan og spørring

CTE

Hvis du ser nøye på den, opp til versjon 12 (eller starter fra den med nøkkelordet MATERIALIZED) formasjon CTE er en absolutt barriere for planleggeren.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Dette betyr at hvis vi ser en CTE-generasjon et sted i forespørselen og en node et sted i planen CTE, så "kjemper" disse nodene definitivt med hverandre, vi kan umiddelbart kombinere dem.

Problem med en stjerne: CTE-er kan nestes.
PostgreSQL Query Profiler: hvordan matche plan og spørring
Det er svært dårlig nestede, og til og med de med samme navn. For eksempel kan du inne CTE A gjøre CTE X, og på samme nivå inne CTE B gjør det 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å dette "med øynene" - til og med å se planen, til og med se forespørselens kropp - er veldig vanskelig. Hvis CTE-generasjonen din er kompleks, nestet og forespørslene er store, så er den helt ubevisst.

UNION

Hvis vi har et nøkkelord i spørringen UNION [ALL] (operatør for å slå sammen to prøver), så tilsvarer det i planen enten en node Append, eller noen Recursive Union.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Det som er "over" ovenfor UNION - dette er den første etterkommeren av noden vår, som er "under" - den andre. Hvis gjennom UNION vi har flere blokker "limt" på en gang, da Append-det vil fortsatt bare være en node, men den vil ikke ha to, men mange barn - i den rekkefølgen de går:

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

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

Problem med en stjerne: inne i rekursiv sampling generering (WITH RECURSIVE) kan også være mer enn én UNION. Men bare den aller siste blokken etter den siste er alltid rekursiv UNION. Alt ovenfor er ett, men annerledes UNION:

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

Du må også kunne "stikke ut" slike eksempler. I dette eksemplet ser vi det UNION-Det var 3 segmenter i forespørselen vår. Følgelig en UNION соответствует Append-node, og til den andre - Recursive Union.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Lese-skrive data

Alt er lagt ut, nå vet vi hvilken del av forespørselen som tilsvarer hvilken del av planen. Og i disse stykkene kan vi enkelt og naturlig finne de objektene som er "lesbare".

Fra et spørringssynspunkt vet vi ikke om det er en tabell eller en CTE, men de er utpekt av samme node RangeVar. Og når det gjelder "lesbarhet", er dette også et ganske begrenset sett med noder:

  • 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 kjenner strukturen til planen og spørringen, vi kjenner korrespondansen til blokkene, vi kjenner navnene på objektene - vi gjør en en-til-en sammenligning.
PostgreSQL Query Profiler: hvordan matche plan og spørring

En gang til oppgave "med en stjerne". Vi tar forespørselen, utfører den, vi har ingen aliaser - vi leser den bare to ganger fra samme CTE.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Vi ser på planen – hva er problemet? Hvorfor hadde vi et alias? Vi bestilte det ikke. Hvor får han tak i et slikt "nummernummer"?

PostgreSQL legger det til selv. Du trenger bare å forstå det akkurat et slikt alias for oss, for å sammenligne med planen, gir det ingen mening, det er bare lagt til her. La oss ikke ta hensyn til ham.

Den andre oppgave "med en stjerne": hvis vi leser fra en partisjonert tabell, får vi en node Append eller Merge Append, som vil bestå av et stort antall "barn", og hver av dem vil være på en eller annen måte Scan'om fra tabelldelen: Seq Scan, Bitmap Heap Scan eller Index Scan. Men i alle fall vil disse "barna" ikke være komplekse spørsmål - slik kan disse nodene skilles fra AppendUNION.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Vi forstår også slike knuter, samler dem "i en haug" og sier: "alt du leser fra megatable er her og ned i treet".

"Enkle" datamottaksnoder

PostgreSQL Query Profiler: hvordan matche plan og spørring

Values Scan samsvarer i planen VALUES i forespørselen.

Result er en forespørsel uten FROM slags SELECT 1. Eller når du har et bevisst falskt uttrykk i WHERE-block (da vises attributtet 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 "kart" til SRF-ene med samme navn.

Men med nestede søk er alt mer komplisert - dessverre blir de ikke alltid til InitPlan/SubPlan. Noen ganger blir de til ... Join eller ... Anti Join, spesielt når du skriver noe sånt som WHERE NOT EXISTS .... Og her er det ikke alltid mulig å kombinere dem - i teksten til planen er det ingen operatører som tilsvarer planens noder.

En gang til oppgave "med en stjerne": noen VALUES i forespørselen. I dette tilfellet og i planen vil du få flere noder Values Scan.
PostgreSQL Query Profiler: hvordan matche plan og spørring

"Nummererte" suffikser vil bidra til å skille dem fra hverandre - de legges til nøyaktig i den rekkefølgen de tilsvarende er funnet VALUES-blokker langs forespørselen fra topp til bunn.

Databehandling

Det ser ut til at alt i forespørselen vår har blitt sortert ut - alt som gjenstår er Limit.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Men her er alt enkelt - slike noder som Limit, Sort, Aggregate, WindowAgg, Unique "kart" en-til-en til de tilsvarende operatørene i forespørselen, hvis de er der. Det er ingen "stjerner" eller vanskeligheter her.
PostgreSQL Query Profiler: hvordan matche plan og spørring

BLI

Vanskeligheter oppstår når vi ønsker å kombinere JOIN seg imellom. Dette er ikke alltid mulig, men det er mulig.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Fra spørringsparserens synspunkt har vi en node JoinExpr, som har nøyaktig to barn - venstre og høyre. Dette er følgelig det som er "over" din JOIN og det som er skrevet "under" i forespørselen.

Og fra planens synspunkt er dette to etterkommere av noen * Loop/* Join-node. Nested Loop, Hash Anti Join,... - noe sånt.

La oss bruke enkel logikk: hvis vi har tabellene A og B som "blir sammen" i planen, kan de i forespørselen bli plassert enten A-JOIN-BEller B-JOIN-A. La oss prøve å kombinere på denne måten, la oss prøve å kombinere omvendt, og så videre til vi går tom for slike par.

La oss ta syntakstreet vårt, ta planen vår, se på dem... ikke like!
PostgreSQL Query Profiler: hvordan matche plan og spørring

La oss tegne det på nytt i form av grafer - åh, det ser allerede ut som noe!
PostgreSQL Query Profiler: hvordan matche plan og spørring

La oss merke oss at vi har noder som samtidig har barn B og C - vi bryr oss ikke i hvilken rekkefølge. La oss kombinere dem og snu bildet av noden.
PostgreSQL Query Profiler: hvordan matche plan og spørring

La oss se igjen. Nå har vi noder med barn A og par (B + C) - også kompatible med dem.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Flott! Det viser seg at vi er disse to JOIN fra forespørselen med planen nodene ble vellykket kombinert.

Akk, dette problemet er ikke alltid løst.
PostgreSQL Query Profiler: hvordan matche plan og spørring

For eksempel hvis du er i en forespørsel A JOIN B JOIN C, og i planen ble først og fremst de "ytre" nodene A og C koblet til. Men det er ingen slik operatør i forespørselen, vi har ingenting å markere, ingenting å legge et hint til. Det er det samme med «komma» når du skriver A, B.

Men i de fleste tilfeller kan nesten alle nodene "løsnes", og du kan få denne typen profilering til venstre i tide - bokstavelig talt, som i Google Chrome når du analyserer JavaScript-kode. Du kan se hvor lang tid det tok å "utføre" hver linje og hver setning.
PostgreSQL Query Profiler: hvordan matche plan og spørring

Og for å gjøre det mer praktisk for deg å bruke alt dette, har vi laget lagring arkiv, hvor du kan lagre og senere finne planene dine sammen med tilhørende forespørsler eller dele koblingen med noen.

Hvis du bare trenger å bringe et uleselig søk i en tilstrekkelig form, bruk vår "normalisator".

PostgreSQL Query Profiler: hvordan matche plan og spørring

Kilde: www.habr.com

Legg til en kommentar