PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Parechji chì sò digià usu spiegà.tensor.ru - u nostru serviziu di visualizazione di u pianu PostgreSQL pò esse micca cunnisciutu di unu di i so superpoteri - turnendu un pezzu difficiuli di leghje di u logu di u servitore...

PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda
... in una dumanda bella cuncepita cù suggerimenti contextuali per i nodi di u pianu currispundenti:

PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda
In questa trascrizione di a seconda parte di u so rapportu à PGConf.Russia 2020 Vi dicu cumu avemu riisciutu à fà questu.

A trascrizione di a prima parte, dedicata à i prublemi tipici di prestazione di query è e so suluzioni, ponu esse truvati in l'articulu "Ricette per e dumande SQL malate".



Prima, cuminciamu a culurite - è ùn avemu micca più culore di u pianu, l'avemu digià culurita, l'avemu digià bella è comprensibile, ma una dumanda.

Ci pareva chì cun un "fogliu" cusì senza formate a dumanda tirata da u logu pare assai brutta è per quessa inconveniente.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

In particulare quandu i sviluppatori "colanu" u corpu di a dumanda in u codice (questu hè, sicuru, un antipattern, ma succede) in una linea. Horrible!

Disegnemu questu in una manera più bella.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

È se pudemu disegnà stu bellu, vale à dì, disassemble è riunite u corpu di a dumanda, allora pudemu "attacà" un suggerimentu à ogni ughjettu di sta dumanda - ciò chì hè accadutu à u puntu currispundente in u pianu.

Query syntax tree

Per fà questu, a dumanda deve esse prima analizata.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Perchè avemu u core di u sistema corre nantu à NodeJS, allora avemu fattu un modulu per questu, pudete truvà lu in GitHub. In fatti, questi sò estesi "bindings" à l'internu di u parser PostgreSQL stessu. Vale à dì, a grammatica hè simplicemente compilata binaria è ligami sò fatti da NodeJS. Avemu pigliatu i moduli di l'altri cum'è una basa - ùn ci hè micca un grande sicretu quì.

Avemu alimentatu u corpu di a dumanda cum'è input à a nostra funzione - à l'output avemu un arbulu di sintassi analizatu in forma di un oggettu JSON.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Avà pudemu passà per questu arbre in a direzzione opposta è assemble una dumanda cù l'indentazioni, u culore è u furmatu chì vulemu. No, questu ùn hè micca persunalizabile, ma ci paria chì questu seria cunvene.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Mapping query and plan nodes

Avà vedemu cumu pudemu cumminà u pianu chì avemu analizatu in u primu passu è a quistione chì avemu analizatu in u sicondu.

Pigliemu un esempiu simplice - avemu una dumanda chì genera un CTE è leghje da ellu duie volte. Ellu genera un tali pianu.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

CTE

Se guardate attentamente, finu à a versione 12 (o partendu da questu cù a keyword MATERIALIZED) furmazione CTE hè una barriera assoluta per u pianificatore.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Questu significa chì si vede una generazione CTE in un locu in a dumanda è un node in un locu in u pianu CTE, Allora sti nodi definitamente "lottanu" cù l'altri, pudemu immediatamente cumminà.

Prublemu cù un asteriscu: I CTE ponu esse nidificati.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda
Ci sò assai pocu nidificatu, è ancu quelli di u stessu nome. Per esempiu, pudete dentru CTE ACTE X, è à u listessu livellu internu CTE B fà di novu CTE X:

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

Quandu si compara, duvete capisce questu. Capisce questu "cù i vostri ochji" - ancu vede u pianu, ancu vede u corpu di a dumanda - hè assai difficiule. Se a vostra generazione CTE hè cumplessa, nidificata, è e dumande sò grandi, allora hè completamente inconsciente.

UNION

Se avemu una keyword in a dumanda UNION [ALL] (operatore di unisce dui campioni), poi in u pianu currisponde sia à un nodu Append, o certi Recursive Union.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Ciò chì hè "sopra" sopra UNION - questu hè u primu discendente di u nostru node, chì hè "sottu" - u sicondu. Se attraversu UNION avemu parechji blocchi "incollati" à una volta, allora Append- Ci sarà sempre un solu node, ma ùn averà micca dui, ma parechji zitelli - in l'ordine chì vanu, rispettivamente:

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

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

Prublemu cù un asteriscu: generazione di campionamentu recursivu (WITH RECURSIVE) pò ancu esse più di unu UNION. Ma solu l'ultimu bloccu dopu à l'ultimu hè sempre recursive UNION. Tuttu sopra hè unu, ma sfarente UNION:

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

Avete ancu bisognu di pudè "stà fora" tali esempi. In questu esempiu avemu vistu chì UNION- Ci era 3 segmenti in a nostra dumanda. Per quessa, unu UNION соответствует Append-node, è à l'altru - Recursive Union.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Dati di lettura-scrittura

Tuttu hè dispostu, avà sapemu quale pezzu di a dumanda currisponde à quale pezzu di u pianu. È in questi pezzi pudemu truvà facilmente è naturalmente quelli ogetti chì sò "leghjite".

Da un puntu di vista di quistione, ùn sapemu micca s'ellu hè una tavola o un CTE, ma sò designati da u stessu node. RangeVar. È in termini di "leggibilità", questu hè ancu un settore abbastanza limitatu di nodi:

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

Sapemu a struttura di u pianu è a dumanda, sapemu a currispundenza di i blocchi, sapemu i nomi di l'uggetti - facemu un paragone unu à unu.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Di novu tâche "avec un astérisque". Pigliemu a dumanda, eseguite, ùn avemu micca alias - avemu solu leghje duie volte da u stessu CTE.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Fighjemu u pianu - chì hè u prublema? Perchè avemu avutu un alias? Ùn avemu micca urdinatu. Induve uttene un tali "numeru numeru"?

PostgreSQL l'aghjunghje stessu. Basta à capisce chì ghjustu un tali alias per noi, per u scopu di paragunà cù u pianu, ùn hà micca sensu, hè solu aghjuntu quì. Ùn fate micca attente à ellu.

U sicondu tâche "avec un astérisque": se leghjemu da una tavola partizionata, allora averemu un node Append o Merge Append, chì sarà custituitu da un gran numaru di "figlioli", è ognunu di quale serà in qualchì manera Scan'om da a sezione di tabella: Seq Scan, Bitmap Heap Scan o Index Scan. Ma, in ogni casu, questi "figlioli" ùn saranu micca dumande cumplessu - questu hè cumu si ponu distingue questi nodi da Append at UNION.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Capemu ancu tali nodi, cullà "in una pila" è dicenu: "tuttu ciò chì leghje da megatable hè quì è in l'arburu".

Nodi "semplici" di riceve dati

PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Values Scan currisponde à u pianu VALUES in a dumanda.

Result hè una dumanda senza FROM un tipu di SELECT 1. O quandu avete una espressione deliberatamente falsa in WHERE-block (poi l'attributu appare 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 "mappa" à i SRF di u listessu nome.

Ma cù e dumande nidificate tuttu hè più cumplicatu - sfurtunatamenti, ùn sò micca sempre diventati InitPlan/SubPlan. Calchì volta si trasformanu in ... Join o ... Anti Join, soprattuttu quandu scrivi qualcosa cum'è WHERE NOT EXISTS .... È quì ùn hè micca sempre pussibule cunghjuntà - in u testu di u pianu ùn ci sò micca operatori chì currispondenu à i nodi di u pianu.

Di novu tâche "avec un astérisque": un pò VALUES in a dumanda. In questu casu è in u pianu uttene parechji nodi Values Scan.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

I suffissi "numerati" aiutanu à distinguelli l'una di l'altru - sò aghjuntu esattamente in l'ordine in quale si trovanu i currispondenti. VALUES-blocks longu a dumanda da cima à fondu.

Trattamentu di dati

Sembra chì tuttu in a nostra dumanda hè stata risolta - tuttu ciò chì resta hè Limit.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Ma quì tuttu hè simplice - tali nodi cum'è Limit, Sort, Aggregate, WindowAgg, Unique "mappa" unu à unu à l'operatori currispundenti in a dumanda, se ci sò. Ùn ci hè micca "stella" o difficultà quì.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

JOIN

I difficultà si sviluppanu quandu vulemu cumminà JOIN trà elli. Questu ùn hè micca sempre pussibule, ma hè pussibule.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Da u puntu di vista di u parser di query, avemu un node JoinExpr, chì hà esattamente dui figlioli - left and right. Questu, dunque, hè ciò chì hè "sopra" u vostru JOIN è ciò chì hè scrittu "sottu" in a dumanda.

È da u puntu di vista di u pianu, questi sò dui discendenti di certi * Loop/* Join-node. Nested Loop, Hash Anti Join,... - qualcosa cusì.

Utilizemu una logica simplice: s'ellu avemu i tavule A è B chì "uniscenu" in u pianu, allora in a dumanda puderanu esse situati sia A-JOIN-B, o B-JOIN-A. Pruvemu di cumminà stu modu, pruvemu à cumminà l'altru, è cusì finu à chì avemu da esse fora di tali coppie.

Pigliamu u nostru arbulu di sintassi, pigliate u nostru pianu, fighjateli... micca simili !
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Ridighjemu in forma di grafici - oh, pare digià qualcosa!
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Avemu nutà chì avemu nodes chì simultaneamente anu figlioli B è C - ùn importa micca in quale ordine. Cumbinemu è vultà a stampa di u node.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Fighjemu di novu. Avà avemu node cù i zitelli A è parigli (B + C) - cumpatibili ancu cun elli.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Perfettu! Risulta chì simu sti dui JOIN da a dumanda cù i nodi di u pianu sò stati cumminati cun successu.

Alas, stu prublema ùn hè micca sempre risolta.
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Per esempiu, se in una dumanda A JOIN B JOIN C, è in u pianu, prima di tuttu, i nodi "esterni" A è C sò stati cunnessi. Ma ùn ci hè micca un tali operatore in a dumanda, ùn avemu nunda di mette in risaltu, nunda di aghjunghje un suggerimentu. Hè listessa cù a "virgula" quandu scrivi A, B.

Ma, in a maiò parte di i casi, quasi tutti i nodi ponu esse "slegati" è pudete uttene stu tipu di prufilu à a manca in u tempu - literalmente, cum'è in Google Chrome quandu analizà u codice JavaScript. Pudete vede quantu tempu ogni linea è ogni dichjarazione hà pigliatu per "eseguite".
PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

È per fà più còmuda per voi di utilizà tuttu questu, avemu fattu u almacenamentu archiviu, induve pudete salvà è dopu truvà i vostri piani cù e dumande assuciate o sparte u ligame cù qualchissia.

Se avete solu bisognu di purtà una dumanda illegibile in una forma adatta, aduprate u nostru "normalizzatore".

PostgreSQL Query Profiler: Cumu currisponde à u pianu è a dumanda

Source: www.habr.com

Add a comment