PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Monet, jotka jo käyttävät selittää.tensor.ru - PostgreSQL-suunnitelman visualisointipalvelumme ei ehkä ole tietoinen yhdestä sen supervoimista - kääntää vaikealukuisen palan palvelinlokista...

PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen
... kauniisti suunniteltuun kyselyyn, jossa on kontekstuaalisia vihjeitä vastaaville suunnitelmasolmuille:

PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen
Tässä hänen toisen osan transkriptiossa raportti PGConf.Russia 2020 -tapahtumassa Kerron sinulle, kuinka onnistuimme tässä.

Ensimmäisen osan transkriptio, joka on omistettu tyypillisille kyselyn suorituskykyongelmille ja niiden ratkaisuille, löytyy artikkelista "Reseptejä vaikeisiin SQL-kyselyihin".



Ensin aloitetaan väritys - emmekä enää väritä suunnitelmaa, olemme jo värittäneet sen, meillä on jo kaunis ja ymmärrettävä, mutta pyyntö.

Meistä näytti, että tällaisella muotoilemattomalla "arkilla" tukista vedetty pyyntö näyttää erittäin rumalta ja siksi hankalalta.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Varsinkin kun kehittäjät "liimaavat" pyynnön rungon koodiin (tämä on tietysti antipattern, mutta sitä tapahtuu) yhdelle riville. Kamalaa!

Piirretään tämä jotenkin kauniimmin.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Ja jos voimme piirtää tämän kauniisti, eli purkaa ja koota pyynnön rungon, voimme sitten "liittää" tämän pyynnön jokaiseen kohteeseen vihjeen - mitä tapahtui vastaavassa suunnitelman kohdassa.

Kyselyn syntaksipuu

Tätä varten pyyntö on ensin jäsennettävä.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Koska meillä on järjestelmän ydin toimii NodeJS:llä, sitten teimme sille moduulin, voit löytää sen GitHubista. Itse asiassa nämä ovat laajennettuja "sidoksia" itse PostgreSQL-jäsentimen sisäisiin osiin. Toisin sanoen kielioppi on yksinkertaisesti käännetty binäärimuotoon ja siihen tehdään sidoksia NodeJS:stä. Otimme pohjaksi muiden ihmisten moduulit - tässä ei ole suurta salaisuutta.

Syötämme pyynnön rungon syötteeksi funktioomme - lähdössä saamme jäsennetyn syntaksipuun JSON-objektin muodossa.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Nyt voimme ajaa tämän puun läpi vastakkaiseen suuntaan ja koota pyynnön haluamillamme sisennyksillä, värityksillä ja muotoiluilla. Ei, tämä ei ole muokattavissa, mutta meistä tuntui, että tämä olisi kätevää.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Kartoituskyselyn ja suunnitelman solmut

Katsotaan nyt, kuinka voimme yhdistää ensimmäisessä vaiheessa analysoimamme suunnitelman ja toisessa vaiheessa analysoimamme kyselyn.

Otetaan yksinkertainen esimerkki - meillä on kysely, joka luo CTE:n ja lukee sen kahdesti. Hän laatii sellaisen suunnitelman.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

CTE

Jos katsot sitä huolellisesti, versioon 12 asti (tai alkaen siitä avainsanalla MATERIALIZED) muodostus CTE on ehdoton este suunnittelijalle.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Tämä tarkoittaa, että jos näemme CTE-sukupolven jossain pyynnössä ja solmun jossain suunnitelmassa CTE, niin nämä solmut ehdottomasti "taistelevat" keskenään, voimme yhdistää ne välittömästi.

Ongelma tähdellä: CTE:t voidaan sisäkkäin sijoittaa.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen
On erittäin huonosti sisäkkäisiä, ja jopa samanniisiä. Voit esimerkiksi sisälle CTE A tehdä CTE X, ja samalla tasolla sisällä CTE B tee se uudestaan CTE X:

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

Kun vertailet, sinun on ymmärrettävä tämä. Tämän "silmin" ymmärtäminen - jopa suunnitelman näkeminen, jopa pyynnön rungon näkeminen - on erittäin vaikeaa. Jos CTE-sukupolvesi on monimutkainen, sisäkkäinen ja pyynnöt ovat suuria, se on täysin tiedostamaton.

UNIONIN

Jos kyselyssä on avainsana UNION [ALL] (kahden näytteen yhdistämisen operaattori), silloin se vastaa suunnitelmassa jompaakumpaa solmua Append, tai joitain Recursive Union.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Se mikä on "ylhäällä" yllä UNION - tämä on solmumme ensimmäinen jälkeläinen, joka on "alla" - toinen. Jos läpi UNION meillä on siis useita lohkoja "liimattu" kerralla Append- siellä on edelleen vain yksi solmu, mutta sillä ei ole kaksi, vaan monta lasta - tässä järjestyksessä:

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

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

Ongelma tähdellä: rekursiivisen näytteenoton sisällä (WITH RECURSIVE) voi myös olla useampi kuin yksi UNION. Mutta vain viimeinen lohko viimeisen jälkeen on aina rekursiivinen UNION. Kaikki yllä oleva on yhtä, mutta erilaista UNION:

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

Sinun on myös kyettävä "työstämään" tällaisia ​​esimerkkejä. Tässä esimerkissä näemme sen UNION-pyynnössämme oli 3 segmenttiä. Vastaavasti yksi UNION соответствует Append-solmulle ja toiselle - Recursive Union.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Luku-kirjoitustiedot

Kaikki on laadittu, nyt tiedämme, mikä pyynnön osa vastaa mitä suunnitelman osaa. Ja näistä teoksista löydämme helposti ja luonnollisesti ne esineet, jotka ovat "luettavia".

Kyselyn näkökulmasta emme tiedä, onko se taulukko vai CTE, mutta ne on merkitty samalla solmulla. RangeVar. Ja "luetettavuuden" kannalta tämä on myös melko rajoitettu joukko solmuja:

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

Tiedämme suunnitelman ja kyselyn rakenteen, tiedämme lohkojen vastaavuuden, tiedämme objektien nimet - teemme vertailun yksitellen.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

uudelleen tehtävä "tähdellä". Otamme pyynnön, suoritamme sen, meillä ei ole aliaksia - luimme sen vain kahdesti samalta CTE:ltä.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Katsomme suunnitelmaa - mikä on ongelma? Miksi meillä oli alias? Emme tilannut sitä. Mistä hän saa sellaisen "numeronumeron"?

PostgreSQL lisää sen itse. Sinun tarvitsee vain ymmärtää se juuri sellainen alias meille suunnitelmaan verrattuna siinä ei ole mitään järkeä, se vain lisätään tähän. Älkäämme kiinnittäkö häneen huomiota.

Toinen tehtävä "tähdellä": jos luemme osioidusta taulukosta, saamme solmun Append tai Merge Append, joka koostuu suuresta määrästä "lapsia", ja joista jokainen tulee olemaan jollakin tavalla Scan'om osiotaulukosta: Seq Scan, Bitmap Heap Scan tai Index Scan. Mutta joka tapauksessa nämä "lapset" eivät ole monimutkaisia ​​kyselyitä - näin nämä solmut voidaan erottaa Append at UNION.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Ymmärrämme myös sellaiset solmut, keräämme ne "yhdeksi pinoksi" ja sanomme: "kaikki mitä luet megatableista on täällä ja alas puuhun".

"Yksinkertaiset" dataa vastaanottavat solmut

PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Values Scan vastaa suunnitelmaa VALUES pyynnössä.

Result on pyyntö ilman FROM eräänlainen SELECT 1. Tai kun sinulla on tarkoituksella väärä ilmaus WHERE-block (attribuutti tulee näkyviin 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 "kartta" samannimisille SRF:ille.

Mutta sisäkkäisillä kyselyillä kaikki on monimutkaisempaa - valitettavasti ne eivät aina muutu InitPlan/SubPlan. Joskus ne muuttuvat ... Join tai ... Anti Join, varsinkin kun kirjoitat jotain sellaista WHERE NOT EXISTS .... Ja täällä ei ole aina mahdollista yhdistää niitä - suunnitelman tekstissä ei ole suunnitelman solmuja vastaavia operaattoreita.

uudelleen tehtävä "tähdellä": jonkin verran VALUES pyynnössä. Tässä tapauksessa ja suunnitelmassa saat useita solmuja Values Scan.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

"Numeroidut" jälkiliitteet auttavat erottamaan ne toisistaan ​​- ne lisätään täsmälleen siinä järjestyksessä, jossa vastaavat löytyvät VALUES-lohkot pyynnön varrella ylhäältä alas.

Tietojenkäsittely

Näyttää siltä, ​​​​että kaikki pyyntömme on selvitetty - jäljellä on vain Limit.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Mutta täällä kaikki on yksinkertaista - sellaiset solmut kuin Limit, Sort, Aggregate, WindowAgg, Unique "kartoittaa" yksitellen pyynnön vastaaviin operaattoreihin, jos niitä on. Tässä ei ole "tähtiä" tai vaikeuksia.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

LIITY

Vaikeuksia syntyy, kun haluamme yhdistää JOIN keskenään. Tämä ei ole aina mahdollista, mutta mahdollista.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Kyselyn jäsentäjän näkökulmasta meillä on solmu JoinExpr, jolla on täsmälleen kaksi lasta - vasen ja oikea. Tämä on siis se, mikä on JOIN-tunnuksesi "yläpuolella" ja mitä on kirjoitettu "sen alapuolelle" pyynnössä.

Ja suunnitelman näkökulmasta nämä ovat joidenkin kaksi jälkeläistä * Loop/* Join-solmu. Nested Loop, Hash Anti Join,... - jotain sellaista.

Käytetään yksinkertaista logiikkaa: jos meillä on taulukot A ja B, jotka "liittyvät" toisiinsa suunnitelmassa, niin pyynnössä ne voisivat sijaita joko A-JOIN-BTai B-JOIN-A. Yritetään yhdistää tällä tavalla, yritetään yhdistää toisinpäin ja niin edelleen, kunnes tällaiset parit loppuvat.

Otetaan syntaksipuumme, suunnitelmamme, katsotaan niitä... ei samanlaisia!
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Piirretään se uudelleen kaavioiksi - oi, se näyttää jo joltakin!
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Huomattakoon, että meillä on solmuja, joissa on samanaikaisesti lapset B ja C – emme välitä missä järjestyksessä. Yhdistetään ne ja käännetään solmun kuva ympäri.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Katsotaanpa uudestaan. Nyt meillä on solmuja, joissa on lapsia A ja pareja (B + C) - yhteensopivia myös heidän kanssaan.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Loistava! Osoittautuu, että olemme nämä kaksi JOIN Pyynnöstä suunnitelman solmut yhdistettiin onnistuneesti.

Valitettavasti tätä ongelmaa ei aina ratkaista.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Esimerkiksi jos pyynnössä A JOIN B JOIN C, ja suunnitelmassa ensinnäkin yhdistettiin "ulommat" solmut A ja C. Mutta pyynnössä ei ole sellaista operaattoria, meillä ei ole mitään korostettavaa, ei mitään, mihin liittää vihjettä. Sama on "pilkun" kanssa kirjoittaessasi A, B.

Mutta useimmissa tapauksissa melkein kaikki solmut voidaan "irrottaa" ja saat tällaisen profiloinnin ajoissa vasemmalle - kirjaimellisesti, kuten Google Chromessa, kun analysoit JavaScript-koodia. Voit nähdä, kuinka kauan kunkin rivin ja jokaisen lauseen "suoritus" kesti.
PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Ja jotta sinun olisi helpompi käyttää tätä kaikkea, olemme tehneet säilytystilaa arkisto, jossa voit tallentaa ja myöhemmin löytää suunnitelmasi ja niihin liittyvät pyynnöt tai jakaa linkin jonkun kanssa.

Jos sinun on vain saatava lukukelvoton kysely sopivaan muotoon, käytä meidän "normalisaattorimme".

PostgreSQL Query Profiler: suunnitelman ja kyselyn yhdistäminen

Lähde: will.com

Lisää kommentti