Identifizieren der Abfrageverarbeitungskomponenten
Es gibt vier separate Phasen zum Ausführen der Abfrage. In der Reihenfolge der Ausführung sind die Phasen:
- Analyse
- Transformieren (Rewriter)
- Planung
- Ausführung
Der Parser
Der Parser ist für das Überprüfen der Abfragezeichenfolge auf gültige Syntax verantwortlich. Der Parser besteht aus zwei Hauptteilen:
- gram.y, bestehend aus einer Reihe von Grammatikregeln und entsprechenden Aktionen
- scan.1, der Lexer, der Bezeichner und SQL-Schlüsselwörter erkennt. Jedes Schlüsselwort und jeder Bezeichner führt dazu, dass ein Token erstellt und an den Parser übergeben wird.
Der Parser erstellt eine Abfragestruktur, welche die Abfrage in identifizierbare Teile aufteilt, damit Sie verstehen, welche Tabellen beteiligt sind, welche Filter angewendet wurden usw. Die Teile einer Abfragestruktur lauten:
- Befehlstyp: SELECT, INSERT, UPDATE oder DELETE.
- Bereichstabelleneintrag (Range Table Entry, RTE) – eine Liste der Beziehungen,
ie
-Tabellen, Unterabfragen, Ergebnisse von Verknüpfungen usw. In einer SELECT-Anweisung werden diese Elemente nach dem FROM-Schlüsselwort angezeigt. - Ergebnisbeziehung: Bei der Ergebnisbeziehung für die Befehle INSERT, UPDATE und DELETE handelt es sich um die Tabelle oder Ansicht, in der die Änderungen vorgenommen werden.
- Zielliste: Die Ergebnisse der Abfrage, die zwischen den Schlüsselwörtern SELECT und FROM identifiziert wurden. DELETE-Befehle erzeugen kein Ergebnis, sodass der Planner einen speziellen Eintrag hinzufügt, damit der Executor die zu löschende Zeile findet. INSERT-Befehle identifizieren die neuen Zeilen, die in die Ergebnisbeziehung eingefügt werden sollen. Für UPDATE-Befehle beschreibt die Zielliste die neuen Zeilen, die die alten ersetzen sollten.
- Qualifikation: Ein boolescher Wert, der angibt, ob der Vorgang für die endgültige Ergebniszeile ausgeführt werden soll. Er entspricht der WHERE-Klausel einer SQL-Anweisung.
- Verknüpfungsstruktur: Diese Struktur könnte eine Liste der FROM-Elemente sein. Verknüpfungen können in einer beliebigen Reihenfolge oder in einer bestimmten Reihenfolge erfolgen, wie z. B. äußere Verknüpfungen.
- Andere: Elemente, die in dieser Phase nicht relevant sind, z. B. die ORDER BY-Klausel.
Rewriter
Die Ausgabe des Parsers wird an den Transformations- oder Rewriterprozess übergeben, es sei denn, ein Fehler tritt auf. In diesem Fall wird eine Fehlermeldung zurückgegeben.
Der Abfragerewriter schreibt den Abfragetext neu, indem Regeln darauf angewendet werden. Der Rewriter berücksichtigt Regeln und übergibt dann die geänderte Abfrage an den Abfrageplanner. Die Sicherheit auf Zeilenebene wird in dieser Phase implementiert.
Beispielsweise werden Regeln für SELECT immer als letzter Schritt angewendet. Dies gilt auch für INSERT-, UPDATE- und DELETE-Abfragen. Regeln bedeuten auch, dass UPDATE-Abfragen vorhandene Zeilen nicht überschreiben, sondern eine neue Zeile eingefügt und die alte Zeile ausgeblendet wird. Nachdem die Transaktion committet ist, kann der Vakuumprozess die ausgeblendete Zeile entfernen.
Planner
Der Planner muss die Abfrageregeln verwenden und ermitteln, auf welche Weise die Abfrage am schnellsten ausgeführt werden kann.
Er erstellt eine Planstruktur mit Knoten, die physische Vorgänge darstellen, die die Daten involvieren.
PostgreSQL verwendet einen kostenbasierten Abfrageoptimierer, um den optimalen Plan für eine Abfrage zu finden. Der Planner bewertet verschiedene Ausführungspläne und schätzt, wie viel der erforderlichen Ressourcen benötigt werden, z. B. CPU-Zyklen, E/A-Vorgänge usw. Diese Schätzung wird dann in Einheiten umgewandelt, die als Plankosten bezeichnet werden. Der Plan mit den niedrigsten Kosten wird ausgewählt.
Wenn jedoch die Anzahl der Verknüpfungen wächst, erhöht sich die Anzahl der möglichen Pläne exponentiell. Das Auswerten aller möglichen Pläne wird auch für relativ einfache Abfragen unmöglich. Heuristik und Algorithmen werden verwendet, um die Anzahl der möglichen Pläne zu begrenzen. Das führt dazu, dass der ausgewählte Plan eventuell nicht der optimale Plan ist. Er ist jedoch nahezu optimal und wird in einer angemessenen Zeit ausgewählt.
Die Kosten sind die beste Schätzung des Planners. Der Zweck der Kostenschätzung besteht darin, unterschiedliche Ausführungspläne für dieselbe Abfrage unter den gleichen Bedingungen zu vergleichen. Der Planner verwendet Statistiken, die in Tabellen und Zeilen erfasst werden, um Kostenschätzungen für Abfragen zu erzeugen. Damit die Kostenschätzungen genau werden, müssen Statistiken auf dem neuesten Stand sein.
Aktuelle Statistiken
Die Plannerkomponente des Abfrageoptimierers verwendet Statistiken zu Tabellen und Zeilen, um genaue Kostenschätzungen zu erzeugen.
ANALYZE erfasst Statistiken über den Inhalt von Datenbanktabellen und speichert die Ergebnisse im Systemkatalog pg_statistic. Sie müssen ANALYZE ausführen, wenn:
- Sie autovacuum (das Tabellen normalerweise automatisch analysiert) deaktiviert haben
- Sie autovacuum deaktiviert und ANALYZE in letzter Zeit nicht ausgeführt haben
- Eine der vorherigen Optionen, und es gibt viele INSERTS-, UPDATES- oder DELETE-Anweisungen.
Kostenschätzungen beruhen auf aktuelle Statistiken. Wenn Statistiken veraltet sind, könnte ein ineffizienter Plan ausgewählt werden. Wenn kein Parameter an ANALYZE übergeben wird, wird jede Tabelle in der Datenbank untersucht.
Die Syntax für ANALYZE lautet:
ANALYZE [ VERBOSE ] [ ***table*** [ ( ***column*** [, ...] ) ] ]
VERBOSE zeigt Fortschrittsmeldungen an, um anzuzeigen, welche Tabelle analysiert wird, sowie einige Statistiken.
Planen Sie VACUUM und ANALYZE für eine tägliche Ausführung während einer geringen Nutzungszeit ein. ANALYZE kann parallel zu anderen Aktivitäten ausgeführt werden, da nur eine Lesesperre der Zieltabelle benötigt wird.
Ausführendes Konto
In dieser Phase wird der vom Planner erstellte Plan rekursiv verarbeitet, um die erforderlichen Zeilen zu extrahieren. Jedes Mal, wenn ein Planknoten aufgerufen wird, muss der Executor eine Zeile bereitstellen oder zurückmelden, dass das Bereitstellen abgeschlossen ist.
Der Executor bewertet alle vier SQL-Abfragetypen:
- SELECT
- INSERT
- UPDATE
- DELETE
Für SELECT gibt der Executor jede Zeile als Resultset an den Client zurück.
Für INSERT wird jede zurückgegebene Zeile in die angegebene Tabelle eingefügt. Dieser Vorgang erfolgt in einem speziellen Planknoten namens „ModifyTable“ auf oberster Ebene.
Für UPDATE enthält jede berechnete Zeile alle aktualisierten Spaltenwerte sowie die Zeilen-ID der Zielzeile. Die Daten werden an einen ModifyTable-Knoten gesendet, der eine aktualisierte Zeile erstellt und die alte Zeile als gelöscht markiert.
Für DELETE wird die Zeilen-ID als einzige Spalte vom Plan zurückgegeben. Der ModifyTable-Knoten verwendet die Zeilen-ID, um die Zeile als gelöscht zu markieren.