PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Viele, die es bereits nutzen erklären.tensor.ru - Unser PostgreSQL-Planvisualisierungsdienst ist sich möglicherweise einer seiner Superkräfte nicht bewusst - einen schwer lesbaren Teil des Serverprotokolls umzuwandeln ...

PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an
… in eine schön gestaltete Abfrage mit Kontexthinweisen für die relevanten Planknoten:

PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an
In dieser Abschrift des zweiten Teils seines Bericht auf der PGConf.Russia 2020 Ich werde Ihnen sagen, wie wir es geschafft haben.

Das Transkript des ersten Teils, der sich mit typischen Abfrageleistungsproblemen und deren Lösungen befasst, finden Sie im Artikel „Rezepte für kranke SQL-Abfragen“.



Machen wir zuerst die Farbgebung – und wir werden den Plan nicht mehr ausmalen, wir haben ihn bereits gemalt, er ist schon schön und verständlich, aber die Bitte.

Es schien uns, dass die mit einem unformatierten „Blatt“ aus dem Protokoll gezogene Anfrage sehr hässlich und daher unpraktisch aussieht.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Vor allem, wenn Entwickler den Anforderungstext im Code (das ist natürlich ein Anti-Pattern, aber es kommt vor) in einer Zeile „kleben“. Grusel!

Lass es uns irgendwie schöner zeichnen.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Und wenn wir es schön zeichnen können, also den Anfragekörper zerlegen und wieder zusammensetzen können, dann können wir dann jedem Objekt dieser Anfrage einen Hinweis „anhängen“ – was an der entsprechenden Stelle im Plan passiert ist.

Abfragesyntaxbaum

Dazu muss die Abfrage zunächst geparst werden.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Weil wir es haben Der Kern des Systems läuft auf NodeJS, dann haben wir ein Modul dafür erstellt, das können Sie finde es auf Github. Tatsächlich handelt es sich hierbei um erweiterte „Bindungen“ zu den Interna des PostgreSQL-Parsers selbst. Das heißt, die Grammatik wird einfach binär kompiliert und von NodeJS werden Bindungen daran vorgenommen. Wir haben die Module anderer Leute als Grundlage genommen – hier gibt es kein großes Geheimnis.

Wir geben den Hauptteil der Eingabeanforderung an unsere Funktion weiter – am Ausgang erhalten wir einen analysierten Syntaxbaum in Form eines JSON-Objekts.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Jetzt können wir diesen Baum in umgekehrter Richtung durchlaufen und die Anfrage mit den gewünschten Einzügen, Farben und Formatierungen zusammenstellen. Nein, das ist nicht konfigurierbar, aber wir dachten, dass dies praktisch wäre.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Abfrage- und Planknoten zuordnen

Sehen wir uns nun an, wie wir den Plan, den wir im ersten Schritt analysiert haben, und die Abfrage, die wir im zweiten Schritt analysiert haben, kombinieren können.

Nehmen wir ein einfaches Beispiel: Wir haben eine Anfrage, die einen CTE erstellt und ihn zweimal liest. Er erstellt einen solchen Plan.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

CTE

Wenn man es genau betrachtet, ist das vor der 12. Version (oder ab dieser mit dem Schlüsselwort MATERIALIZED) Formation CTE ist eine unbedingte Barriere für den Planer.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Und das heißt, wenn wir irgendwo in der Anfrage die Generierung von CTE und irgendwo im Plan den Knoten sehen CTE, dann „kämpfen“ diese Knoten eindeutig untereinander, wir können sie sofort kombinieren.

Aufgabe „mit Sternchen“Hinweis: CTEs können verschachtelt sein.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an
Es gibt sehr schlecht verschachtelte und sogar gleichnamige. Zum Beispiel können Sie drinnen CTE A сделать CTE X, und innen auf gleicher Höhe CTE B wiederholen CTE X:

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

Beim Vergleich müssen Sie dies verstehen. Es ist sehr schwierig, dies mit „Augen“ zu verstehen – selbst wenn man den Plan sieht, sogar den Text der Anfrage. Wenn Ihre CTE-Generierung komplex und verschachtelt ist und die Anforderungen groß sind, erfolgt sie völlig unbewusst.

UNION

Wenn wir ein Schlüsselwort in der Anfrage haben UNION [ALL] (Operator zum Verbinden zweier Stichproben), dann entspricht er im Plan einem der beiden Knoten Append, oder einige Recursive Union.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Das, was „oben“ ist UNION - Dies ist das erste Kind unseres Knotens, das "von unten" ist - das zweite. Wenn durch UNION wir haben also mehrere Blöcke auf einmal „geklebt“. Append-node wird immer noch nur einen haben, aber nicht zwei Kinder, sondern viele – in der Reihenfolge, wie sie gehen:

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

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

Aufgabe „mit Sternchen“: innerhalb der rekursiven Abrufgenerierung (WITH RECURSIVE) kann auch mehr als eins sein UNION. Aber nur der allerletzte Block nach dem letzten ist immer rekursiv UNION. Alles oben ist eins, aber anders UNION:

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

Solche Beispiele müssen auch „einfügen“ können. In diesem Beispiel sehen wir das UNION-Segmente in unserer Anfrage waren 3 Stück. Dementsprechend eins UNION Streichhölzer Append-Knoten und der andere - Recursive Union.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Daten lesen/schreiben

Alles klar, jetzt wissen wir, welcher Teil der Anfrage welchem ​​Teil des Plans entspricht. Und in diesen Stücken können wir leicht und natürlich jene Objekte finden, die „lesbar“ sind.

Aus Sicht der Abfrage wissen wir nicht, ob es sich um eine Tabelle oder einen CTE handelt, aber sie werden durch denselben Knoten bezeichnet RangeVar. Und im „lesbaren“ Plan ist dies auch eine ziemlich begrenzte Anzahl von Knoten:

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

Wir kennen die Struktur des Plans und der Anfrage, wir kennen die Entsprechung der Blöcke, wir kennen die Namen der Objekte – wir führen einen eindeutigen Vergleich durch.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Schon wieder Aufgabe „mit einem Sternchen“. Wir nehmen eine Anfrage entgegen, führen sie aus, wir haben keine Aliase – wir lesen sie einfach zweimal von einem CTE.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Schauen wir uns den Plan an – wo ist das Problem? Warum hatten wir einen Alias? Wir haben es nicht bestellt. Woher hat er so eine „Nummer“?

PostgreSQL fügt es selbst hinzu. Das muss man einfach verstehen einfach so ein Pseudonym Für uns macht es zum Vergleich mit dem Plan keinen Sinn, es wird hier einfach hinzugefügt. Schenken wir ihm keine Aufmerksamkeit.

Die zweite Aufgabe „mit einem Sternchen“: Wenn wir aus einer partitionierten Tabelle lesen, erhalten wir einen Knoten Append oder Merge Append, das aus einer großen Anzahl von „Kindern“ bestehen wird, und jedes davon wird einige sein Scan'om aus dem Tabellenbereich: Seq Scan, Bitmap Heap Scan oder Index Scan. Aber auf jeden Fall handelt es sich bei diesen „Kindern“ nicht um komplexe Abfragen – so können diese Knoten unterschieden werden Append при UNION.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Wir verstehen solche Knoten auch, wir sammeln sie „auf einem Stapel“ und sagen: „Alles, was Sie von Megatable lesen, finden Sie hier und weiter unten".

„Einfache“ Datenerfassungsknoten

PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Values Scan im Plan entspricht VALUES in der Anfrage.

Result ist eine Anfrage ohne FROM wie SELECT 1. Oder wenn Sie einen falschen Ausdruck haben WHERE-block (dann erscheint das Attribut 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 „Mapyatsya“ im gleichnamigen SRF.

Aber bei verschachtelten Abfragen ist alles komplizierter – leider werden sie nicht immer zu InitPlan/SubPlan. Manchmal verwandeln sie sich in ... Join oder ... Anti Join, besonders wenn du so etwas schreibst wie WHERE NOT EXISTS .... Und es ist nicht immer möglich, dort zu kombinieren - im Text des Plans gibt es keine Operatoren, die den Knoten des Plans entsprechen.

Schon wieder Aufgabe „mit einem Sternchen“: manche VALUES in der Anfrage. In diesem Fall und im Plan erhalten Sie mehrere Knoten Values Scan.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

„Zahlen“-Suffixe helfen dabei, sie voneinander zu unterscheiden – sie werden genau in der Reihenfolge hinzugefügt, in der die entsprechenden VALUES-Blöcke im Verlauf der Anfrage von oben nach unten.

Datenverarbeitung

Es scheint, dass alles in unserer Anfrage geklärt wurde – nur Limit.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Aber hier ist alles einfach – solche Knoten wie Limit, Sort, Aggregate, WindowAgg, Unique Sie „ordnen“ eins zu eins den entsprechenden Operatoren in der Anfrage zu, sofern diese vorhanden sind. Es gibt keine „Sterne“ und keine Schwierigkeiten.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

JOIN

Schwierigkeiten entstehen, wenn wir uns verbinden wollen JOIN untereinander. Das ist nicht immer möglich, aber es ist möglich.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Aus Sicht des Abfrageparsers haben wir einen Knoten JoinExpr, das genau zwei Kinder hat – links und rechts. Dies ist jeweils das, was „über“ Ihrem JOIN steht und was „darunter“ in der Abfrage geschrieben wird.

Und aus der Sicht des Plans sind dies zwei Nachkommen von einigen * Loop/* Join-Knoten. Nested Loop, Hash Anti Join. . . ist so etwas.

Lassen Sie uns eine einfache Logik verwenden: Wenn wir die Tabellen A und B haben, die im Plan miteinander „verknüpft“ sind, dann könnten sie in der Abfrage beides sein A-JOIN-BOder B-JOIN-A. Versuchen wir, auf diese Weise zu kombinieren, versuchen wir, auf die entgegengesetzte Weise zu kombinieren und so weiter, bis solche Paare aufgebraucht sind.

Nehmen Sie unseren Syntaxbaum, nehmen Sie unseren Plan, schauen Sie sie sich an ... es sieht nicht danach aus!
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Lassen Sie es uns in Form von Diagrammen neu zeichnen – oh, etwas ist schon etwas ähnlich geworden!
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Beachten wir, dass wir Knoten haben, die gleichzeitig die Kinder B und C haben – in welcher Reihenfolge spielt es für uns keine Rolle. Lassen Sie uns sie kombinieren und das Bild des Knotens umdrehen.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Schauen wir noch einmal. Jetzt haben wir Knoten mit Kindern A und Paaren (B + C) – kompatibel mit ihnen.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Großartig! Es stellt sich heraus, dass wir diese beiden sind JOIN aus der Abfrage mit den Knoten des Plans wurden erfolgreich kombiniert.

Leider ist dieses Problem nicht immer gelöst.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Zum Beispiel, wenn die Anfrage A JOIN B JOIN C, und im Plan wurden zunächst die „extremen“ Knoten A und C verbunden. Aber es gibt keinen solchen Operator in der Abfrage, wir haben nichts hervorzuheben, es gibt nichts, woran wir den Hinweis binden könnten. Das Gleiche gilt für „Komma“, wenn Sie schreiben A, B.

Aber in den meisten Fällen gelingt es fast allen Knoten, sich rechtzeitig zu „entbinden“ und ein solches Profiling auf der linken Seite zu erhalten – im wahrsten Sinne des Wortes, wie in Google Chrome, wenn Sie JavaScript-Code analysieren. Sie können sehen, wie lange jede Zeile und jede Anweisung „ausgeführt“ wurde.
PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Und um Ihnen die Nutzung all dessen zu erleichtern, haben wir einen Speicher geschaffen das Archiv, wo Sie Ihre Pläne speichern und dann zusammen mit zugehörigen Abfragen finden oder einen Link mit jemandem teilen können.

Wenn Sie nur eine unleserliche Anfrage in eine angemessene Form bringen müssen, verwenden Sie unser „Normalisierer“.

PostgreSQL Query Profiler: So passen Sie Plan und Abfrage an

Source: habr.com

Kommentar hinzufügen