PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Molts que ja estan utilitzant explicar.tensor.ru - És possible que el nostre servei de visualització del pla PostgreSQL no conegui un dels seus superpoders: convertir una part difícil de llegir del registre del servidor...

PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta
... en una consulta ben dissenyada amb consells contextuals per als nodes del pla corresponents:

PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta
En aquesta transcripció de la segona part del seu informe a PGConf.Russia 2020 Us explicaré com ho hem aconseguit.

La transcripció de la primera part, dedicada als problemes típics de rendiment de les consultes i les seves solucions, es pot trobar a l'article "Receptes per a consultes SQL amb problemes".



Primer, comencem a pintar, i ja no pintarem el plànol, ja l'hem pintat, ja el tenim bonic i comprensible, però una petició.

Ens va semblar que amb un "full" tan sense format la sol·licitud extreta del registre sembla molt lletja i, per tant, incòmode.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Sobretot quan els desenvolupadors "enganxen" el cos de la sol·licitud al codi (això és, per descomptat, un antipatró, però passa) en una línia. Horrible!

Dibuixem-ho d'alguna manera més bella.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

I si ho podem dibuixar molt bé, és a dir, desmuntar i tornar a reunir el cos de la sol·licitud, aleshores podem "adjuntar" una pista a cada objecte d'aquesta sol·licitud: què va passar al punt corresponent del pla.

Consulta l'arbre de sintaxi

Per fer-ho, primer s'ha d'analitzar la sol·licitud.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Perquè tenim el nucli del sistema s'executa a NodeJS, llavors vam fer un mòdul per a això, pots trobar-lo a GitHub. De fet, aquests són "enllaços" estesos a les parts internes del mateix analitzador de PostgreSQL. És a dir, la gramàtica és simplement compilada en binària i s'hi fan enllaços des de NodeJS. Hem agafat com a base els mòduls d'altres persones: aquí no hi ha cap gran secret.

Introduïm el cos de la sol·licitud com a entrada de la nostra funció: a la sortida obtenim un arbre de sintaxi analitzat en forma d'objecte JSON.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Ara podem recórrer aquest arbre en la direcció oposada i muntar una sol·licitud amb els sagnats, el color i el format que volem. No, això no és personalitzable, però ens va semblar que seria convenient.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Consulta de mapes i nodes de planificació

Ara veiem com podem combinar el pla que hem analitzat en el primer pas i la consulta que hem analitzat en el segon.

Prenguem un exemple senzill: tenim una consulta que genera un CTE i el llegeix dues vegades. Ell genera aquest pla.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

CTE

Si ho mireu amb atenció, fins a la versió 12 (o començant amb la paraula clau MATERIALIZED) formació CTE és una barrera absoluta per al planificador.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Això vol dir que si veiem una generació CTE en algun lloc de la sol·licitud i un node en algun lloc del pla CTE, llavors aquests nodes definitivament "barallen" entre ells, els podem combinar immediatament.

Problema amb un asterisc: els CTE es poden imbricar.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta
N'hi ha de molt mal nidificats, i fins i tot de homònims. Per exemple, pots dins CTE A fer CTE X, i al mateix nivell interior CTE B fer-ho de nou CTE X:

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

Quan compareu, heu d'entendre això. Entendre això "amb els ulls" - fins i tot veure el pla, fins i tot veure el cos de la petició - és molt difícil. Si la vostra generació CTE és complexa, imbricada i les sol·licituds són grans, aleshores és completament inconscient.

UNIÓ

Si tenim una paraula clau a la consulta UNION [ALL] (operador d'unir dues mostres), llavors en el pla correspon a un node Append, o alguns Recursive Union.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

El que està "a dalt" a dalt UNION - aquest és el primer descendent del nostre node, que està "a sota" - el segon. Si passa UNION tenim diversos blocs "enganxats" alhora, doncs Append-encara hi haurà un sol node, però no tindrà dos, sinó molts fills - en l'ordre en què van, respectivament:

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

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

Problema amb un asterisc: generació de mostreig recursiu dins (WITH RECURSIVE) també pot ser més d'un UNION. Però només l'últim bloc després de l'últim sempre és recursiu UNION. Tot el que hi ha a dalt és un, però diferent UNION:

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

També heu de ser capaços de "desmarcar" aquests exemples. En aquest exemple ho veiem UNION-Hi havia 3 segments a la nostra sol·licitud. En conseqüència, un UNION correspon Append-node, i a l'altre - Recursive Union.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Dades de lectura-escriptura

Tot està disposat, ara sabem quina peça de la sol·licitud correspon a quina peça del pla. I en aquestes peces podem trobar de manera fàcil i natural aquells objectes que són "llegibles".

Des del punt de vista de la consulta, no sabem si és una taula o un CTE, però estan designats pel mateix node RangeVar. I en termes de "llegibilitat", aquest també és un conjunt força limitat de nodes:

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

Coneixem l'estructura del plànol i la consulta, coneixem la correspondència dels blocs, coneixem els noms dels objectes, fem una comparació un a un.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

De nou tasca "amb un asterisc". Prenem la sol·licitud, l'executem, no tenim cap àlies; només la llegim dues vegades des del mateix CTE.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Mirem el pla: quin és el problema? Per què teníem un àlies? No l'hem encarregat. D'on treu aquest "número número"?

PostgreSQL l'afegeix ell mateix. Només cal entendre això només un àlies així per a nosaltres, a efectes de comparació amb el pla, no té cap sentit, simplement s'afegeix aquí. No li fem cas.

El segon tasca "amb un asterisc": si estem llegint des d'una taula particionada, obtindrem un node Append o Merge Append, que constarà d'un gran nombre de "nens", i cadascun dels quals serà d'alguna manera Scan'om de la secció de la taula: Seq Scan, Bitmap Heap Scan o Index Scan. Però, en qualsevol cas, aquests "fills" no seran consultes complexes; així és com es poden distingir aquests nodes de Append en UNION.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

També entenem aquests nusos, els recollim "en una pila" i diem: "tot el que llegiu de megatable és aquí i avall de l'arbre".

Nodes de recepció de dades "simples".

PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Values Scan correspon al pla VALUES en la petició.

Result és una petició sense FROM tipus de SELECT 1. O quan tens una expressió deliberadament falsa WHERE-block (aleshores apareix l'atribut 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 "mapa" als SRF del mateix nom.

Però amb les consultes imbricades tot és més complicat; malauradament, no sempre es converteixen en InitPlan/SubPlan. De vegades es converteixen en ... Join o ... Anti Join, sobretot quan escrius alguna cosa així WHERE NOT EXISTS .... I aquí no sempre és possible combinar-los: al text del pla no hi ha operadors corresponents als nodes del pla.

De nou tasca "amb un asterisc": alguns VALUES en la petició. En aquest cas i en el pla obtindreu diversos nodes Values Scan.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Els sufixos "numerats" ajudaran a distingir-los els uns dels altres: s'afegeixen exactament en l'ordre en què es troben els corresponents VALUES-bloques al llarg de la sol·licitud de dalt a baix.

Processament de dades

Sembla que tot el que hi ha a la nostra sol·licitud s'ha resolt; tot el que queda és Limit.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Però aquí tot és senzill: nodes com Limit, Sort, Aggregate, WindowAgg, Unique “mapa” un a un als operadors corresponents a la sol·licitud, si hi són. Aquí no hi ha "estrelles" ni dificultats.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

JOIN

Les dificultats sorgeixen quan volem combinar JOIN entre ells mateixos. Això no sempre és possible, però sí.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Des del punt de vista de l'analitzador de consultes, tenim un node JoinExpr, que té exactament dos fills: esquerra i dreta. Això, en conseqüència, és el que hi ha "a sobre" del vostre JOIN i el que hi ha escrit "a sota" a la sol·licitud.

I des del punt de vista del pla, es tracta de dos descendents d'alguns * Loop/* Join-node. Nested Loop, Hash Anti Join,... - Alguna cosa així.

Utilitzem una lògica senzilla: si tenim taules A i B que "s'uneixen" entre si al pla, a la sol·licitud es podrien localitzar o bé A-JOIN-BO B-JOIN-A. Intentem combinar d'aquesta manera, intentem combinar a l'inrevés, i així successivament fins que ens quedem sense aquestes parelles.

Agafem el nostre arbre de sintaxi, prenem el nostre pla, mirem-los... no semblants!
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Redibuixem-lo en forma de gràfics: oh, ja sembla alguna cosa!
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Tingueu en compte que tenim nodes que tenen simultàniament fills B i C; no ens importa en quin ordre. Combinem-los i donem la volta a la imatge del node.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Tornem a mirar. Ara tenim nodes amb fills A i parells (B + C), també compatibles amb ells.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Genial! Resulta que som aquests dos JOIN de la sol·licitud amb els nodes del pla es van combinar amb èxit.

Per desgràcia, aquest problema no sempre es resol.
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Per exemple, si en una sol·licitud A JOIN B JOIN C, i al pla, en primer lloc, es van connectar els nodes "exteriors" A i C. Però no hi ha aquest operador a la sol·licitud, no tenim res a destacar, res a què adjuntar una pista. Passa el mateix amb la "coma" quan escrius A, B.

Però, en la majoria dels casos, gairebé tots els nodes es poden "deslligar" i podeu obtenir aquest tipus de perfils a l'esquerra a temps, literalment, com a Google Chrome quan analitzeu el codi JavaScript. Podeu veure quant de temps va trigar cada línia i cada instrucció a "executar-se".
PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

I per fer-te més còmode l'ús de tot això, hem fet emmagatzematge Arxiu, on podeu desar i després trobar els vostres plans juntament amb les sol·licituds associades o compartir l'enllaç amb algú.

Si només necessiteu portar una consulta il·legible en una forma adequada, feu servir el nostre "normalitzador".

PostgreSQL Query Profiler: Com fer coincidir el pla i la consulta

Font: www.habr.com

Afegeix comentari