PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

In protte dy't al brûke explain.tensor.ru - ús PostgreSQL-planfisualisaasjetsjinst is miskien net bewust fan ien fan syn supermacht - in dreech te lêzen stik fan it serverlog draaie ...

PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme
... yn in prachtich ûntworpen query mei kontekstuele hints foar de oerienkommende planknooppunten:

PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme
Yn dit transkripsje fan it twadde diel fan syn rapport by PGConf.Russia 2020 Ik sil jo fertelle hoe't wy dit slagge binne.

It transkripsje fan it earste diel, wijd oan typyske query-prestaasjesproblemen en har oplossingen, is te finen yn it artikel "Rezept foar sike SQL-fragen".



Lit ús earst begjinne te kleuren - en wy sille it plan net mear kleurje, wy hawwe it al kleure, wy hawwe it al moai en begryplik, mar in fersyk.

It like ús dat mei sa'n unformattearre "blêd" it fersyk dat út it log is lutsen tige ûnsjogge sjocht en dêrom ûngemaklik.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Benammen as ûntwikkelders it lichem fan 'e oanfraach yn' e koade "lymje" (dit is fansels in antipattern, mar it bart) yn ien rigel. Ferskriklik!

Litte wy dit op ien of oare manier moaier tekenje.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

En as wy dit prachtich kinne tekenje, dat is, it lichem fan 'e oanfraach demontearje en wer byinoar sette, dan kinne wy ​​dan in hint "heakje" oan elk objekt fan dit fersyk - wat barde op it korrespondearjende punt yn it plan.

Query syntaksis beam

Om dit te dwaan, moat it fersyk earst parsed wurde.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Om't wy hawwe de kearn fan it systeem rint op NodeJS, dan hawwe wy der in module foar makke, dat kinst fyn it op GitHub. Yn feite binne dit útwreide "bindingen" oan 'e ynterne fan' e PostgreSQL-parser sels. Dat is, de grammatika is gewoan binêr gearstald en bindingen wurde makke fanút NodeJS. Wy namen de modules fan oaren as basis - d'r is gjin grut geheim hjir.

Wy fiede it lichem fan it fersyk as ynfier nei ús funksje - by de útfier krije wy in parsed syntaksisbeam yn 'e foarm fan in JSON-objekt.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

No kinne wy ​​​​troch dizze beam yn 'e tsjinoerstelde rjochting rinne en in fersyk gearstalle mei de ynspringen, kleurjen en opmaak dy't wy wolle. Nee, dit is net oanpasber, mar it like ús dat dit handich wêze soe.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Mapping query- en planknooppunten

Litte wy no sjen hoe't wy it plan kinne kombinearje dat wy yn 'e earste stap analysearren en de query dy't wy yn' e twadde analysearre hawwe.

Litte wy in ienfâldich foarbyld nimme - wy hawwe in query dy't in CTE genereart en dêrút twa kear lêst. Hy generearret sa'n plan.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

CTE

As jo ​​der goed nei sjogge, oant ferzje 12 (of begjinnend mei it kaaiwurd MATERIALIZED) formaasje CTE is in absolute barriêre foar de planner.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Dit betsjut dat as wy sjogge in CTE generaasje earne yn it fersyk en in knooppunt earne yn it plan CTE, dan dizze knopen definityf "fjochtsje" mei elkoar, wy kinne se fuortendaliks kombinearje.

Probleem mei in asterisk: CTEs kinne wurde nested.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme
D'r binne tige min geneste, en sels mei deselde namme. Jo kinne bygelyks binnen CTE A meitsje CTE X, en op itselde nivo binnen CTE B Doch it nochris CTE X:

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

As jo ​​​​fergelykje, moatte jo dit begripe. Dit "mei jo eagen" begripe - sels it plan sjen, sels it lichem fan it fersyk sjen - is heul lestich. As jo ​​CTE-generaasje kompleks is, nested, en de oanfragen binne grut, dan is it folslein ûnbewust.

UNION

As wy in kaaiwurd hawwe yn 'e query UNION [ALL] (operator fan it ferbinen fan twa samples), dan komt it yn it plan oerien mei of in knooppunt Append, of guon Recursive Union.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Dat wat "boppe" is UNION - dit is de earste neisiet fan ús knooppunt, dat is "ûnder" - de twadde. As troch UNION wy hawwe ferskate blokken "lym" tagelyk, dan Append-d'r sil noch mar ien knooppunt wêze, mar it sil net twa hawwe, mar in protte bern - yn 'e folchoarder dy't se geane, respektivelik:

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

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

Probleem mei in asterisk: binnen rekursive sampling generaasje (WITH RECURSIVE) kin ek mear as ien wêze UNION. Mar allinnich it alderlêste blok nei it lêste is altyd rekursyf UNION. Alles hjirboppe is ien, mar oars UNION:

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

Jo moatte ek sokke foarbylden "útstekke" kinne. Yn dit foarbyld sjogge wy dat UNION-d'r wiene 3 segminten yn ús fersyk. As gefolch, ien UNION соответствует Append-node, en nei de oare - Recursive Union.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Lês-skriuwe gegevens

Alles is oanlein, no witte wy hokker stik fan it fersyk oerienkomt mei hokker stik fan it plan. En yn dizze stikken kinne wy ​​maklik en natuerlik dy objekten fine dy't "lêsber" binne.

Ut in query eachpunt, wy witte net oft it is in tabel of in CTE, mar se wurde oanwiisd troch deselde knooppunt RangeVar. En yn termen fan "lêsberens" is dit ek in frij beheinde set knopen:

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

Wy kenne de struktuer fan it plan en de query, wy kenne de korrespondinsje fan 'e blokken, wy kenne de nammen fan' e objekten - wy meitsje in ien-op-ien ferliking.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Wer taak "mei in asterisk". Wy nimme it fersyk, fiere it út, wy hawwe gjin aliassen - wy lêze it gewoan twa kear fan deselde CTE.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Wy sjogge nei it plan - wat is it probleem? Wêrom hawwe wy in alias? Wy hawwe it net besteld. Wêr komt er sa'n "nûmernûmer" wei?

PostgreSQL foeget it sels ta. Jo moatte dat gewoan begripe krekt sa'n alias foar ús hat it foar it fergelykjen mei it plan gjin sin, it wurdt hjir gewoan tafoege. Lit ús gjin omtinken jaan oan him.

De twadde taak "mei in asterisk": as wy lêze fan in ferdielde tabel, dan krije wy in knooppunt Append of Merge Append, dy't sil bestean út in grut oantal "bern", en elk fan dat sil wêze ien of oare wize Scan'om út 'e tabel-seksje: Seq Scan, Bitmap Heap Scan of Index Scan. Mar yn alle gefallen sille dizze "bern" gjin komplekse fragen wêze - dit is hoe't dizze knooppunten kinne wurde ûnderskieden fan Append at UNION.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Wy begripe ek sokke knopen, sammelje se "yn ien stapel" en sizze: "alles wat jo lêze fan megatable is hjir en ûnder de beam".

"Ienfâldige" gegevens ûntfangende knopen

PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Values Scan oerienkomt yn plan VALUES yn it fersyk.

Result is in fersyk sûnder FROM soarte fan SELECT 1. Of as jo in opsetsin falske útdrukking yn hawwe WHERE-block (dan ferskynt it attribút 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 "kaart" nei de SRF's mei deselde namme.

Mar mei geneste queries is alles yngewikkelder - spitigernôch wurde se net altyd yn InitPlan/SubPlan. Soms draaie se yn ... Join of ... Anti Join, benammen as jo skriuwe wat as WHERE NOT EXISTS .... En hjir is it net altyd mooglik om se te kombinearjen - yn 'e tekst fan it plan binne d'r gjin operators dy't oerienkomme mei de knopen fan it plan.

Wer taak "mei in asterisk": guon VALUES yn it fersyk. Yn dit gefal en yn it plan krije jo ferskate knopen Values Scan.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

"Nûmere" efterheaksels sille helpe om se fan elkoar te ûnderskieden - se wurde tafoege krekt yn 'e folchoarder wêryn't de oerienkommende wurde fûn VALUES-blokken lâns it fersyk fan boppe nei ûnderen.

Gegevens ferwurkjen

It liket derop dat alles yn ús fersyk is sorteare - alles wat oer is Limit.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Mar hjir is alles ienfâldich - sokke knopen as Limit, Sort, Aggregate, WindowAgg, Unique "map" ien-op-ien oan de oerienkommende operators yn it fersyk, as se binne der. D'r binne hjir gjin "stjerren" of swierrichheden.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

JOIN

Swierrichheden ûntsteane as wy kombinearje wolle JOIN tusken harsels. Dit is net altyd mooglik, mar it is mooglik.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Fanút it eachpunt fan 'e query-parser hawwe wy in knooppunt JoinExpr, dy't krekt twa bern hat - lofts en rjochts. Dit is dus wat "boppe" jo JOIN is en wat "ûnder" is skreaun yn it fersyk.

En út it eachpunt fan it plan binne dat twa neikommelingen fan guon * Loop/* Join-knooppunt. Nested Loop, Hash Anti Join,... - soksawat.

Litte wy ienfâldige logika brûke: as wy tabellen A en B hawwe dy't elkoar yn it plan "meidwaan", dan kinne se yn it fersyk pleatst wurde A-JOIN-B, of B-JOIN-A. Litte wy besykje dizze manier te kombinearjen, litte wy besykje oarsom te kombinearjen, ensfh.

Litte wy ús syntaksisbeam nimme, ús plan nimme, nei har sjen ... net gelyk!
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Litte wy it opnij tekenje yn 'e foarm fan grafiken - och, it liket al wat!
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Lit ús note dat wy knooppunten hawwe dy't tagelyk bern B en C hawwe - it makket ús net út yn hokker folchoarder. Litte wy se kombinearje en de ôfbylding fan 'e knoop omdraaie.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Litte wy nochris sjen. No hawwe wy knooppunten mei bern A en pearen (B + C) - ek kompatibel mei har.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Grut! It docht bliken dat wy dizze twa binne JOIN út it fersyk mei it plan knopen waarden mei súkses kombinearre.

Och, dit probleem wurdt net altyd oplost.
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Bygelyks, as yn in fersyk A JOIN B JOIN C, en yn it plan wiene earst de "bûtenste" knopen A en C ferbûn. Mar d'r is gjin sa'n operator yn it fersyk, wy hawwe neat om te markearjen, neat om in hint oan te heakjen. It is itselde mei de "komma" as jo skriuwe A, B.

Mar, yn 'e measte gefallen, kinne hast alle knooppunten "ûntbûn" wurde en jo kinne dit soarte profilearring op 'e lofterkant krije - letterlik, lykas yn Google Chrome as jo JavaScript-koade analysearje. Jo kinne sjen hoe lang elke rigel en elke ferklearring duorre om "út te fieren".
PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

En om it foar jo handiger te meitsjen om dit alles te brûken, hawwe wy opslach makke argyf, wêr't jo jo plannen kinne bewarje en letter fine tegearre mei assosjearre oanfragen of de keppeling mei immen diele.

As jo ​​gewoan in ûnlêsbere fraach yn in adekwate foarm bringe moatte, brûk dan ús "normalizer".

PostgreSQL Query Profiler: hoe kinne jo plan en query oerienkomme

Boarne: www.habr.com

Add a comment