Mange som allerede bruker â 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...

âŠen pent formatert forespĂžrsel med kontekstuelle tips om de relevante nodene i planen:

I denne tolkningen av den andre delen av sin 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 .

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.

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.

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.

Siden vĂ„r , laget vi et lite modul til den, dere kan . 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.

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.

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.

CTE
Hvis vi ser nÞye pÄ det, er det frem til versjon 12 (eller fra den med nÞkkelordet MATERIALIZED) dannelse .

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.

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.

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.

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.

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.

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.

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

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 = 1Resultat (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.

Ă
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.

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.

JOIN
Vanskeligheter oppstÄr nÄr vi Þnsker Ä kombinere JOIN med hverandre. Dette er ikke alltid mulig, men det kan gjÞres.

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!

Vi tegner det pĂ„ nytt som grafer â Ă„h, nĂ„ begynner det Ă„ ligne noe!

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.

La oss ta en ny titt. NĂ„ har vi noder med barn A og paret (B + C) â la oss kombinere dem ogsĂ„.

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.

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".

Og for at dere skal kunne bruke alt dette mer behagelig, har vi laget lagring , 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 .

Kilde: habr.com
