Wenn du einen SQL-Query schreibst, legt PostgreSQL nicht einfach direkt los. Erstmal schaltet sich das "Gehirn" ein – der Query Optimizer, der einen Ausführungsplan erstellt. Dieser Plan ist wie eine Route auf einer Karte: PostgreSQL berechnet, welche Schritte in welcher Reihenfolge gemacht werden müssen, um die Daten zu bekommen.
Der Query Optimizer checkt alle möglichen Wege, wie dein Query ausgeführt werden kann: sequentielles Tabellenscannen, Nutzung von Indizes, Filtern, Sortieren usw. Er versucht, den günstigsten Weg (was Ressourcen angeht) zu finden. Das heißt, er sucht einen Kompromiss zwischen Ausführungszeit und Serverressourcen.
Wichtige Parameter des Ausführungsplans
Okay, jetzt geht’s ans Eingemachte – wir schauen uns die Parameter an, die PostgreSQL dir nach EXPLAIN ausspuckt. Fangen wir mit einem einfachen Beispiel an:
EXPLAIN
SELECT * FROM students WHERE age > 20;
Du bekommst dann sowas in der Art zurück:
Seq Scan on students (cost=0.00..35.00 rows=7 width=72)
Filter: (age > 20)
Lass uns diese mysteriösen Begriffe und Zahlen mal auseinandernehmen.
1. cost (Ausführungskosten)
cost – das ist die Schätzung, wie viele Ressourcen für den Query gebraucht werden. Dieser Parameter besteht aus zwei Teilen:
- Startup Cost: Kosten für den Start der Operation (z.B. Index vorbereiten).
- Total Cost: Gesamtkosten für die komplette Ausführung.
Beispiel:
cost=0.00..35.00
0.00– das ist die Startup Cost.35.00– das ist die Total Cost.
Je niedriger der cost, desto lieber nimmt PostgreSQL diesen Plan. Aber: cost ist ein relativer Wert. Er wird nicht in Sekunden oder Millisekunden gemessen, sondern spiegelt die interne Einschätzung von PostgreSQL wider.
2. rows (erwartete Zeilenanzahl)
rows zeigt, wie viele Zeilen PostgreSQL an dieser Stelle erwartet zu verarbeiten oder zurückzugeben. In unserem Beispiel:
rows=7
Das heißt, PostgreSQL schätzt, dass der Filter age > 20 7 Zeilen zurückgibt. Diese Info kommt aus der Statistik, die PostgreSQL über die Tabelle sammelt. Wenn die Statistik veraltet ist, kann die Schätzung danebenliegen. Das kann zu einem weniger optimalen Plan führen.
3. width (Zeilenbreite in Bytes)
width – das ist die durchschnittliche Größe jeder Zeile, die an diesem Punkt zurückgegeben wird, gemessen in Bytes. In unserem Beispiel:
width=72
Das heißt, jede zurückgegebene Zeile ist im Schnitt 72 Bytes groß. width berücksichtigt die Größe der Spalten und eventuelle Overheads wie Zeilen-IDs oder Metadaten.
Das ist so ähnlich wie beim Laden einer App. Wenn sie "schwer" ist (also ein hoher width), dauert das Laden länger, selbst wenn du schnelles Internet hast (das wäre dann der cost).
Beispiel: Ausführungsplan analysieren
Schauen wir uns ein echtes Beispiel an. Angenommen, wir haben eine Tabelle students:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
age INTEGER,
major VARCHAR(50)
);
Und wir führen diesen Query aus:
EXPLAIN
SELECT * FROM students WHERE age > 20 AND major = 'CS';
Das Ergebnis könnte so aussehen:
Seq Scan on students (cost=0.00..42.50 rows=3 width=164)
Filter: ((age > 20) AND (major = 'CS'))
- Seq Scan: PostgreSQL macht ein sequentielles Scannen der Tabelle
students. Das heißt, er geht jede Zeile durch. - cost=0.00..42.50: Kosten für die Ausführung.
Startup Costist0.00, Gesamtkosten42.50. - rows=3: PostgreSQL erwartet, dass der Filter
age > 20 AND major = 'CS'3 Zeilen zurückgibt. - width=164: Jede Zeile ist im Schnitt 164 Bytes groß.
Jetzt weißt du, wie PostgreSQL entscheidet und kannst Schwachstellen in deinen Queries finden. Zum Beispiel: Wenn du einen hohen cost siehst, ist der Query vielleicht zu schwer. Oder wenn rows sehr viele Zeilen anzeigt, solltest du deinen Filter überdenken.
Wie funktioniert cost in der Praxis?
Lass uns mal einen Index auf die Spalte age anlegen:
CREATE INDEX idx_age ON students(age);
Jetzt führen wir den Query nochmal aus:
EXPLAIN
SELECT * FROM students WHERE age > 20 AND major = 'CS';
Das Ergebnis kann sich ändern:
Bitmap Heap Scan on students (cost=4.37..20.50 rows=3 width=164)
Recheck Cond: (age > 20)
Filter: (major = 'CS')
-> Bitmap Index Scan on idx_age (cost=0.00..4.37 rows=20 width=0)
Index Cond: (age > 20)
Was hat sich geändert?
- Statt
Seq Scanwird jetztBitmap Heap Scangenutzt: PostgreSQL sucht erst passende Zeilen im Indexidx_ageund holt sie dann aus der Tabelle. costist deutlich gesunken:Startup Costist jetzt4.37,Total Cost20.50.- Durch den Index ist die Operation effizienter geworden.
Visualisierung: Unterschied zwischen Seq Scan und Index Scan
Hier eine kleine Vergleichstabelle, damit es klarer wird:
| Operation | Einführung | Beispiel |
|---|---|---|
| Seq Scan | Liest die ganze Tabelle | Komplettes Durchgehen aller Zeilen |
| Index Scan | Nutzt einen Index | Schnelles Finden von Zeilen über den Index |
Fallstricke und typische Fehler
Wenn du die Parameter des Ausführungsplans nutzt, sei auf ein paar Überraschungen gefasst. Zum Beispiel bedeutet ein niedriger cost nicht immer die beste Ausführung. Wenn die Datenbankstatistik veraltet ist (z.B. nach einem Massen-Update), kann der Plan ungenau sein. Aktualisiere die Statistik mit dem Befehl ANALYZE. Mehr dazu in der nächsten Vorlesung.
Stell sicher, dass du Indizes da nutzt, wo sie Sinn machen. Aber übertreib es nicht: Indizes brauchen Platz und machen Schreiboperationen langsamer.
GO TO FULL VERSION