Start / Seminare / PostgreSQL Performance und Sicherheit in der Praxis
Modul
Abfrage-Performance und Indizes
11 Kapitel in diesem Modul-Video · Laufzeit
Transkript
Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.
Abfrage-Performance und Indizes
0:00 Im ersten Modul haben wir das Fundament gelegt. Jetzt kommt der Teil, in dem sich die meiste Arbeitszeit tatsächlich verdient: die einzelne Abfrage. Wir lernen zuerst, Ausführungspläne zu lesen — das ist die Grundfertigkeit, ohne die alles Weitere Raterei bliebe. Danach bauen wir Indizes, und zwar nicht irgendwelche, sondern solche, die zu einem echten Zugriffsmuster passen. Wir sehen uns an, was sie kosten.
0:24 Und ganz am Ende steht die vielleicht wichtigste Einsicht des Moduls: Manchmal ist nicht der fehlende Index das Problem, sondern die Form, in der die Abfrage geschrieben wurde.
Wo Abfrage-Performance verloren geht
0:34 Bevor wir messen, klären wir, wonach wir eigentlich suchen — und warum eine Abfrage ein Problem sein kann, obwohl sie das richtige Ergebnis liefert und niemand sich beschwert. In diesem Kapitel legen wir zwei Grundlagen: das Beispielschema, an dem wir das ganze Modul arbeiten, und das Vorgehen, mit dem jede Optimierung abläuft.
0:53 Beides klingt nach Vorbereitung und ist in Wahrheit der Teil, der über den Erfolg entscheidet. Wenn ein System langsam wird, ist der Ruf nach mehr Hardware schnell da. Meistens ist er falsch. Der typische Fall sieht so aus: Eine Abfrage zwingt den Server, eine Million Zeilen zu lesen, um fünfundzwanzig zurückzugeben. Das ist, als würden Sie die gesamte Bibliothek durchblättern, weil Sie den Katalog nicht benutzen.
1:18 Die Abfrage funktioniert einwandfrei — sie ist nur um Größenordnungen teurer, als sie sein müsste. Und dieser Aufwand ist am Ergebnis nicht zu erkennen, sondern nur am Plan. Der entscheidende Punkt ist der zweite: die Häufigkeit. Eine Auswertung, die hundert Millisekunden braucht, klingt harmlos — bis Sie merken, dass sie bei jedem Seitenaufruf läuft.
1:39 Dann konkurriert sie tausendfach mit dem Tagesgeschäft. Der dritte Punkt ist der tückische: Diese Abfragen werden langsamer, ohne dass sich am Code irgendetwas ändert. Sie wachsen einfach mit den Daten. Deshalb ist das Lesen von Plänen keine Spezialdisziplin für den Notfall, sondern etwas, das man regelmäßig tut — am besten, bevor es weh tut.
2:01 Damit wir nicht abstrakt bleiben, arbeiten wir das ganze Modul über an einem Ticketverkauf. Sie brauchen sich die Tabellen nicht auswendig zu merken, aber zwei Verhältnisse lohnen sich zu behalten: Es gibt viele Buchungen, und zu jeder Buchung gehören mehrere Posten. Die Postentabelle ist also die mit Abstand größte, und genau deshalb taucht sie in fast jedem Plan als teuerster Knoten auf.
2:23 Merken Sie sich diese Asymmetrie — sie erklärt die meisten Beispiele in diesem Modul. Diese fünf Schritte sind der rote Faden des Moduls, und Schritt zwei ist der, an dem die meisten Optimierungen scheitern. Der teuerste Knoten ist nämlich fast nie der auffälligste. Eine Sortierung am oberen Ende springt ins Auge, kostet aber oft fast nichts — während drei Zeilen weiter unten still eine Million Zeilen gelesen werden.
2:47 Und beachten Sie den letzten Schritt: Nach jeder Verbesserung liegt der Engpass woanders. Optimierung ist deshalb keine einmalige Aktion, sondern eine Schleife, die man bewusst beendet, wenn es gut genug ist.
Eine langsame Abfrage betrachten
2:59 Fangen wir mit dem an, was jede Optimierung braucht und was regelmäßig übersprungen wird: einer sauberen Ausgangsmessung. Wir nehmen eine ganz gewöhnliche Auswertung, wie sie in jeder zweiten Anwendung steht, und sehen uns an, was der Server tatsächlich tun muss, um sie zu beantworten. Noch verbessern wir nichts. Wir wollen erst wissen, wo die Zeit hingeht — alles andere wäre Herumprobieren mit Zusatzaufwand.
3:23 Unser Ausgangsbeispiel ist bewusst unspektakulär: die größten Buchungen der letzten dreißig Tage, über drei Tabellen verbunden. So etwas steht in jeder zweiten Anwendung. Und genau deshalb taugt es als Beispiel — hier geht es nicht um einen exotischen Fehler, sondern um das ganz normale Handwerk. Wichtig ist die Haltung dabei: Wir wollen die Abfrage jetzt noch nicht verbessern. Wir wollen zuerst wissen, wo die Zeit hingeht.
3:49 Alles andere wäre Herumprobieren mit Zusatzaufwand. Sehen Sie sich das Verhältnis an: Am Ende steht ein LIMIT auf fünfundzwanzig Zeilen. Das ist das, was der Benutzer sieht. Die Frage, um die es in diesem ganzen Modul geht, lautet: Wie viele Zeilen muss der Server anfassen, um diese fünfundzwanzig zu finden? Bei einer gut abgestimmten Datenbank sind es einige tausend. Ohne passenden Index können es Millionen sein.
4:14 Und das Erstaunliche daran ist: Beides fühlt sich beim Ausführen zunächst ähnlich an — der Unterschied zeigt sich erst unter Last oder mit wachsenden Daten. Der interessanteste Befund ist der dritte. Der Datumsfilter steht in der Abfrage ganz vorne, aber der Server kann ihn erst anwenden, nachdem er die Zeilen gelesen hat.
4:33 Er liest also alles und wirft dann den größten Teil wieder weg. Diese Formulierung — lesen und danach verwerfen — ist das Erkennungszeichen für einen fehlenden Index, und sie wird uns noch mehrfach begegnen. Der letzte Punkt ist die Gegenprobe: Die Sortierung sieht aufwendig aus, ist es aber nicht, weil bei fünfundzwanzig gewünschten Zeilen ein besonders sparsames Verfahren greift.
4:55 Der erste Punkt ist die Falle der absoluten Zahl. Hundert Millisekunden sind gut oder schlecht — je nachdem, ob die Abfrage einmal täglich oder tausendmal pro Minute läuft. Ohne Häufigkeit ist eine Laufzeit keine Bewertung. Der dritte Punkt ist reine Messhygiene: Der erste Lauf liest von der Platte, der zweite aus dem Cache.
5:15 Wer die beiden vergleicht, misst nicht seine Optimierung, sondern den Cache. Messen Sie also mehrfach und sagen Sie dazu, welchen Lauf Sie zeigen.
Ausführungspläne lesen
5:24 Jetzt kommt die Kernfertigkeit dieses Moduls, und ich meine das wörtlich: Wer Ausführungspläne lesen kann, braucht nicht zu raten. Das ist der ganze Unterschied zwischen gezielter Optimierung und dem Ausprobieren von Indizes. Wir sehen uns an, in welcher Richtung man einen Plan liest, welche wenigen Angaben wirklich zählen und wie man in wenigen Sekunden den Knoten findet, an dem die Arbeit anfällt.
5:47 Ein Ausführungsplan schreckt beim ersten Mal ab, weil er wie eine Wand aus Text wirkt. Der Trick ist die Leserichtung: von unten nach oben. Ganz unten steht, wie der Server an die Daten kommt, ganz oben das fertige Ergebnis. Und Sie müssen nicht jeden Knotentyp kennen. In der Praxis reichen ein paar wenige, plus die Fähigkeit, den teuersten zu finden.
6:07 Das ist ungefähr wie beim Lesen eines Fahrplans: Sie brauchen nicht das ganze Streckennetz, sondern nur Ihre Verbindung. Schritt zwei ist der wertvollste. Zu jedem Knoten stehen zwei Zahlenpaare da: was der Planner erwartet hat und was tatsächlich passiert ist. Diese beiden gegeneinander zu halten, ist die schnellste Diagnose, die es gibt.
6:28 Liegen sie nah beieinander, arbeitet der Planner mit gutem Wissen — dann liegt das Problem beim Zugriff. Weichen sie um Größenordnungen ab, ist die Statistik das Thema, und kein Index der Welt wird das reparieren. Diese eine Unterscheidung erspart Ihnen viele Irrwege. Zwei Zeilen daraus verdienen besondere Aufmerksamkeit. Die erste ist der Kostenwert — und den bitte nicht als Zeit lesen.
6:52 Es ist eine Vergleichsgröße ohne Einheit, nützlich beim Gegenüberstellen zweier Pläne, sinnlos als absolute Aussage. Die dritte Zeile ist dagegen Ihr wichtigster Hebel: Sie zählt, wie viele Zeilen gelesen und dann weggeworfen wurden. Jede Zeile in dieser Angabe ist Arbeit, die niemandem genutzt hat. Wenn Sie in einem Plan nur eine Zahl ansehen dürfen, dann diese.
7:15 Der Zusatz, auf den es hier ankommt, ist die Puffer-Angabe. Laufzeiten schwanken mit der Tageszeit, mit dem Cache, mit dem, was andere gerade tun. Die Zahl gelesener Seiten schwankt nicht — sie ist die ehrlichste Kennzahl, die Sie bekommen. Wenn Sie nach einer Optimierung zeigen wollen, dass sich wirklich etwas verbessert hat, ist diese Zahl das überzeugendste Argument.
7:36 Und ein Warnhinweis zum Mitnehmen: Bei schreibenden Anweisungen führt diese Messung die Anweisung tatsächlich aus. Also nur innerhalb einer Transaktion, die Sie danach zurückrollen. Diese Aufgabe trainiert genau die Haltung, um die es mir hier geht: begründen statt vermuten. Sie sollen nicht sagen, welche Abfrage sich langsam anfühlt, sondern mit Zahlen aus dem Plan zeigen, wo die Arbeit anfällt.
8:00 Nehmen Sie die Ausbaustufe mit — der Unterschied zwischen erstem und zweitem Lauf ist eine Lektion, die man einmal selbst gesehen haben sollte. Danach interpretiert man Messwerte anders, und zwar für den Rest seines Berufslebens. Der zweite Punkt ist der, der am meisten Zeit kostet: EXPLAIN allein führt die Abfrage gar nicht aus.
8:19 Sie sehen dann nur, was der Planner glaubt — und ausgerechnet die spannende Frage, ob er richtig liegt, bleibt offen. Der dritte Punkt verbindet dieses Modul mit dem vorigen: Sobald ein Plan parallel läuft, gilt der Arbeitsspeicher je Prozess und je Operation. Das ist der Moment, in dem die Rechnung von gestern plötzlich sehr konkret wird.
Indizes untersuchen
8:39 Wir wissen jetzt, wo es klemmt. Zeit für den ersten echten Eingriff — und für den Vergleich davor und danach, der in diesem Modul zur Gewohnheit werden soll. In diesem Kapitel legen wir einen ganz einfachen Index an, sehen uns an, wie sich der Plan dadurch verändert, und lernen dabei die Formulierungen kennen, an denen man im Plan erkennt, dass ein Index tatsächlich benutzt wird.
9:00 Ein Index ist das Stichwortverzeichnis Ihres Buches. Er ändert nichts am Inhalt, aber er erspart Ihnen das Durchblättern. Und wie beim Buch gilt: Er ist nicht kostenlos. Er belegt Platz, er muss bei jeder Änderung mitgepflegt werden, und er hilft nur, wenn er zu dem passt, wonach tatsächlich gesucht wird. Ein Verzeichnis nach Autorennamen nützt nichts, wenn alle nach Jahreszahlen suchen.
9:23 Genau darum geht es in diesem und in den nächsten Kapiteln: nicht möglichst viele Indizes, sondern die passenden. Zwei Zeilen, zwei Botschaften. Die erste legt den Index an — B-Tree, das Standardverfahren, und für Datumsbereiche genau richtig. Die zweite ist die, die regelmäßig vergessen wird: Der Planner muss von seiner neuen Möglichkeit auch erfahren.
9:45 Frische Statistiken sorgen dafür, dass er den Index in seine Rechnung einbezieht. Wer diesen Schritt auslässt, legt einen tadellosen Index an, sieht einen unveränderten Plan und zieht den falschen Schluss. Das Entscheidende steht im dritten Punkt: Die Bedingung wandert von der Zeile „Filter" in die Zeile „Index Cond". Klingt nach einer Nuance, ist aber der ganze Unterschied.
10:08 Vorher wurde gelesen und dann geprüft, jetzt wird gezielt gesucht. Das ist der Sprung vom Durchblättern zum Nachschlagen. Und der letzte Punkt ist die Einladung zum Weitermachen: Sobald dieser Engpass weg ist, wird der nächste sichtbar. Genau so soll es sein — Optimierung schält sich Schicht für Schicht nach innen. Der Kern dieser Aufgabe steckt im Wort „belegen". Es reicht nicht, dass sich etwas schneller anfühlt.
10:34 Sie sollen zwei Pläne nebeneinanderlegen und zeigen: Die Zugriffsart hat sich geändert, es werden weniger Seiten gelesen, und die Laufzeit ist gesunken. Diese drei Nachweise zusammen sind ein Argument, das auch in einer Diskussion mit Kollegen trägt. Ein Bauchgefühl ist es nicht. Der zweite Punkt ist der subtilste und begegnet mir am häufigsten: Sobald Sie die indizierte Spalte in eine Funktion oder Berechnung packen, kann der Server den Index nicht mehr verwenden.
11:02 Die Bedingung passt dann nicht mehr zu dem, was im Index steht. Und der letzte Punkt ist eine Warnung für den Produktivbetrieb: Ein Index wird standardmäßig unter einer Sperre aufgebaut. Auf einer großen, viel genutzten Tabelle legen Sie damit für einige Minuten das Geschäft still — es sei denn, Sie verwenden die nebenläufige Variante.
Zusammengesetzte und partielle Indizes
11:21 Bisher hatten wir eine einzige Bedingung. In der Wirklichkeit sind es fast immer mehrere — Datum und Status, Kunde und Zeitraum, und so weiter. Dafür gibt es zwei sehr unterschiedliche Antworten, und die Wahl zwischen ihnen ist eine Entwurfsentscheidung, keine Geschmacksfrage. Wir sehen uns beide an, bauen sie nebeneinander und lassen den Planner entscheiden — der ist in dieser Frage ein guter Lehrer.
11:44 Der Unterschied lässt sich gut an einem Telefonbuch erklären. Ein zusammengesetzter Index ist die Sortierung nach Nachname und dann Vorname — Sie finden damit beides in einem Zugriff. Ein partieller Index ist etwas anderes: Er lässt von vornherein alle Einträge weg, die nicht interessieren, etwa alle außer einer Stadt. Das Buch wird dadurch dünner, schneller zu durchsuchen und billiger zu pflegen. Welche der beiden Antworten passt, hängt davon ab, wie Ihre Nutzer tatsächlich suchen.
12:13 Der interessante Effekt zeigt sich beim zweiten Index, und zwar im Plan: Die Statusbedingung taucht dort gar nicht mehr auf. Das verwirrt beim ersten Mal, ist aber völlig richtig — sie steckt bereits in der Definition des Index. Es existieren schlicht keine anderen Zeilen darin, die man noch aussortieren müsste. Deshalb ist dieser Index kleiner, und deshalb belastet er Schreibzugriffe auf alle anderen Zeilen überhaupt nicht.
12:38 Die Entscheidungsregel steht in den ersten beiden Punkten: Wechseln die Kombinationen, nehmen Sie den zusammengesetzten. Läuft praktisch alles über einen Statuswert, nehmen Sie den partiellen. Der letzte Punkt ist die technische Voraussetzung, die man kennen muss: Die Reihenfolge der Spalten ist nicht beliebig. Der Index lässt sich nur nutzen, wenn die zuerst genannte Spalte in der Bedingung steht — wie im Telefonbuch, in dem die Suche nach einem Vornamen allein Sie auch nicht weiterbringt.
13:05 Diese Aufgabe zielt auf eine Entwurfsentscheidung, nicht auf ein Kommando. Legen Sie beide Varianten an und lassen Sie den Planner wählen — sein Verhalten ist lehrreicher als jede Regel, die ich Ihnen aufschreiben könnte. Und die Ausbaustufe ist der Punkt, an dem es interessant wird: Vergleichen Sie die Größe. Sie werden sehen, dass die beiden Indizes nicht annähernd gleich viel Platz brauchen — und Platz bedeutet hier auch Pflegeaufwand bei jedem Schreibzugriff.
13:32 Der erste Punkt beschreibt das häufigste Antimuster überhaupt: für jede Spalte ein eigener Index, in der Hoffnung, dass der Server sie schon irgendwie kombinieren wird. Das kann er in Grenzen, aber deutlich schlechter als ein Index, der das Muster direkt abbildet. Der dritte Punkt ist die typische Enttäuschung beim partiellen Index: Wenn die Bedingung nicht wörtlich zur Abfrage passt, wird er nicht verwendet.
13:54 Und der letzte ist reine Hygiene — alte Einzelindizes aufzuräumen, nachdem der neue sie ersetzt hat.
Datenspalten in den Index aufnehmen
14:01 Jetzt gehen wir einen Schritt weiter. Bisher hat der Index dabei geholfen, die richtigen Zeilen zu finden; danach musste der Server sie trotzdem noch aus der Tabelle holen. Es geht aber auch anders: Ein Index kann die Antwort gleich mitliefern. Das ist einer der wenigen Kniffe, bei denen sich der Aufwand im Plan sofort und deutlich zeigt — mit der einen Zeile, auf die Sie dabei achten müssen.
14:23 Bleiben wir beim Stichwortverzeichnis. Normalerweise schlagen Sie dort nach und blättern dann zur genannten Seite. Ein abdeckender Index ist so, als stünde die gesuchte Information bereits im Verzeichnis — Sie sparen sich das Blättern komplett. Technisch heißt das: Der Index enthält neben den Suchspalten auch die Werte, die die Abfrage ausgibt.
14:43 Findet der Server dort alles, was er braucht, rührt er die Tabelle gar nicht mehr an. Das ist der Unterschied zwischen zwei Zugriffen und einem. Der Unterschied zwischen den beiden Teilen ist wichtig. Die Spalten in Klammern sind der Suchschlüssel — danach wird gefiltert und sortiert. Die Spalten hinter INCLUDE werden nur mitgeführt, nicht durchsucht. Diese Trennung hat einen guten Grund: Ein Suchschlüssel muss sortiert gepflegt werden, was Aufwand kostet.
15:10 Mitgeführte Spalten hängen einfach dran. Wer alles in den Schlüssel packt, bezahlt Pflegeaufwand für etwas, das er nie durchsucht. Der zweite Punkt ist die Zeile, auf die Sie im Plan achten sollten: Die Angabe zu den Zugriffen auf die Tabelle steht dann auf null. Das ist der Beweis, dass der Index wirklich abdeckt. Und der vierte Punkt schlägt die Brücke zum ersten Modul: Damit das funktioniert, muss der Server wissen, dass die betroffenen Seiten für alle sichtbar sind — und diese Information pflegt das Aufräumen.
15:40 Ein vernachlässigtes Autovacuum entwertet also genau diesen Vorteil wieder. Die Themen hängen enger zusammen, als es zunächst aussieht. Der erste Punkt ist der wichtigste, weil abdeckende Indizes so verlockend sind: Jede zusätzliche Spalte macht den Index breiter, und breiter heißt mehr Platz, mehr Cache-Verdrängung, mehr Schreibaufwand.
16:00 Das lohnt sich für wenige, häufig laufende Abfragen — nicht flächendeckend. Und der dritte Punkt ist ein Entwurfsfehler mit stiller Wirkung: Eine Spalte, nach der auch gefiltert wird, gehört in den Schlüssel. Steht sie nur hinter INCLUDE, kann der Server sie zum Suchen nicht verwenden.
Indizes für JSON-Daten
16:17 Moderne Anwendungen speichern nicht nur Spalten, sondern auch Dokumente — flexible Merkmale, die nicht bei jedem Datensatz gleich aussehen. PostgreSQL kann das gut, aber die gewohnten Indexverfahren greifen dabei nicht mehr. In diesem Kapitel sehen wir uns an, warum das so ist, welches Verfahren stattdessen zuständig ist und welche Variante davon Sie wählen sollten — denn auch hier gibt es eine Entscheidung, die viele gar nicht kennen.
16:42 JSONB ist praktisch: Sie können flexible Merkmale ablegen, ohne für jedes neue Attribut eine Spalte anzulegen. Der Preis dafür ist die Suche. Ein B-Tree sortiert Werte — das setzt voraus, dass es eine sinnvolle Reihenfolge gibt. Bei einem Dokument gibt es die nicht. Ein GIN-Index dreht die Sache deshalb um: Er merkt sich zu jedem Wert im Dokument, in welchen Zeilen er vorkommt.
17:06 Das ist im Prinzip dasselbe Verfahren, mit dem Suchmaschinen arbeiten — ein umgekehrtes Verzeichnis. Der Operator in der Mitte ist der wichtigste in diesem Zusammenhang: Er fragt, ob das Dokument die angegebenen Schlüssel-Wert-Paare enthält. Ohne Index muss der Server dafür jede einzelne Zeile öffnen und hineinsehen — bei zehntausend Zeilen noch erträglich, bei zehn Millionen nicht mehr.
17:30 Der GIN-Index dreht das um: Er liefert direkt die passenden Zeilen. Und genau daran sehen Sie auch, wann sich der Index lohnt — nämlich dann, wenn Sie diese Art Suche regelmäßig ausführen. Hier steckt eine Entscheidung, die viele gar nicht kennen. Die Standardklasse kann sehr viele Operatoren und ist deshalb größer. Die spezialisierte Variante kann nur das Enthaltensein — dafür ist sie deutlich kompakter und schneller.
17:55 Die Frage, die Sie sich stellen müssen, lautet also schlicht: Suche ich wirklich nur nach dem Enthaltensein? Wenn ja, sparen Sie mit der zweiten Klasse spürbar Platz und Schreibaufwand. Wenn nein, bleiben Sie beim Standard, sonst fehlt Ihnen später ein Zugriffsweg. Der dritte Punkt ist eine Entwurfsfrage, die über dieses Kapitel hinausgeht. JSONB ist ein gutes Werkzeug für wirklich variable Merkmale.
18:19 Es ist ein schlechtes Werkzeug für Dinge, die eigentlich immer da sind und immer denselben Typ haben — dafür gibt es Spalten, mit Typprüfung, Beschränkungen und günstigeren Indizes. Die Flexibilität ist verlockend, aber sie kostet später an vielen Stellen gleichzeitig.
Wenn Indizes die Leistung kosten
18:35 Bis hierhin war jeder Index ein Gewinn. Jetzt sehen wir uns die Rechnung an, und zwar die vollständige. Denn jeder Index, den Sie anlegen, ist ein Versprechen, das bei jeder einzelnen Änderung eingelöst werden muss. Wir machen diesen Aufwand messbar, damit Sie eine Zahl vor Augen haben statt einer vagen Warnung — und wir sehen uns an, wie man Indizes findet, die diesen Preis kosten, ohne etwas dafür zu liefern.
18:59 Jeder Index ist ein Versprechen, das bei jeder Änderung eingelöst werden muss. Fügen Sie eine Zeile ein, wandert sie nicht nur in die Tabelle, sondern in jeden betroffenen Index. Das gilt für Einfügen, Ändern und Löschen gleichermaßen. Dazu kommen Platz auf der Platte, Verdrängung im Cache und zusätzliche Arbeit für das Aufräumen.
19:18 Ein Index verbessert das Lesen und verteuert das Schreiben — das ist kein Nachteil, den man vermeiden könnte, sondern der Handel, den man bewusst eingeht. Der Aufbau dieses Versuchs ist absichtlich schlicht: dieselbe Einfügung, einmal auf einer Tabelle mit nur dem Primärschlüssel und einmal mit einer Handvoll weiterer Indizes.
19:37 Was Sie dabei sehen sollen, ist keine kleine Verschlechterung. Die Laufzeit vervielfacht sich, und die Zahl geschriebener Seiten steigt entsprechend. Ich empfehle, diesen Versuch einmal selbst zu machen — die Zahl, die dabei herauskommt, prägt sich besser ein als jede Warnung von mir. Der dritte Punkt ist der, an den beim Entwurf am seltensten gedacht wird: Dieser Aufwand fällt nicht nur beim nächtlichen Import an, sondern bei jeder einzelnen Änderung im Tagesbetrieb.
20:04 Jede Buchung, jede Statusänderung zahlt mit. Und daraus folgt der Maßstab aus dem letzten Punkt: Ein Index muss sich verdienen. Die Frage ist nicht, ob er irgendwann einmal helfen könnte, sondern ob er ein Lesemuster trägt, das wichtig genug ist, um diesen Preis zu rechtfertigen. Diese Aufgabe ist das Gegenstück zum Indexbau, und sie wird viel zu selten gemacht.
20:25 In fast jeder gewachsenen Datenbank liegen Indizes, die einmal für einen Bericht gebaut wurden, den es nicht mehr gibt. Die Statistiksicht verrät Ihnen, wie oft ein Index tatsächlich benutzt wurde. Und die Ausbaustufe ist wichtig, damit Sie sich nicht selbst täuschen: Diese Zähler laufen seit dem letzten Zurücksetzen. Wenn das gestern war, sagt eine niedrige Zahl gar nichts.
20:48 Der erste Punkt ist ein Kniff, der bei großen Importen viel Zeit spart: Indizes vorher entfernen, danach neu aufbauen. Einmal am Stück aufzubauen ist deutlich günstiger, als jede Zeile einzeln einzupflegen. Der zweite Punkt ist der stille Ballast in gewachsenen Systemen — zwei Indizes, die dieselbe Spaltenfolge abdecken, weil sie in verschiedenen Projekten entstanden sind.
21:10 Der eine kostet voll und bringt nichts.
Abfragen umschreiben
21:13 Und jetzt zu dem Kapitel, in dem der größte Hebel dieses Moduls steckt. Hier geht es nicht um Prozente, sondern um Größenordnungen. Denn manchmal ist nicht der fehlende Index das Problem, sondern die Form, in der die Abfrage geschrieben wurde — eine Formulierung, die fachlich völlig korrekt ist und den Server trotzdem zu tausendfacher Wiederholung derselben Arbeit zwingt.
21:33 Sehen wir uns den Klassiker an. Manchmal ist nicht der fehlende Index das Problem, sondern die Form der Abfrage selbst. Der Klassiker ist die korrelierte Unterabfrage: eine Abfrage, die für jede Zeile der äußeren Abfrage noch einmal ausgeführt wird. Das ist, als würden Sie für jeden Kunden im Laden einzeln ins Lager laufen, statt einmal alles zu holen, was gebraucht wird.
21:55 Fachlich ist die Formulierung völlig korrekt und oft sogar gut lesbar. Sie zwingt den Server nur zu Arbeit, die sich vermeiden lässt. Beide Fassungen liefern dasselbe Ergebnis — das ist der Punkt. Der Unterschied liegt nicht im Resultat, sondern in der Anzahl der Durchgänge. Die erste Fassung geht für jede Buchung erneut in die Postentabelle. Die zweite holt einmal alles und gruppiert danach. Bei zwanzig Buchungen merkt das niemand.
22:21 Bei einigen hundert wird daraus der Unterschied zwischen einer Abfrage, über die sich Nutzer beschweren, und einer, die im normalen Rauschen verschwindet. Das Erkennungszeichen im Plan ist ein eigener Teilplan mit der Angabe, wie oft er ausgeführt wurde. Wenn dort statt einer Eins eine dreistellige Zahl steht, haben Sie den Verursacher gefunden. Besonders aussagekräftig ist der dritte Punkt: die Zahl gelesener Seiten.
22:46 Millionen gegenüber Tausenden — das ist keine Optimierung am Rand, das ist eine andere Größenordnung. Und deshalb lohnt sich der Blick auf die Form einer Abfrage, bevor man den nächsten Index baut. Bei dieser Aufgabe ist mir der Nachweis der Gleichheit besonders wichtig. Beim Umschreiben verändert man leicht die Semantik, ohne es zu merken — dazu gleich mehr.
23:08 Deshalb die Ausbaustufe: Vergleichen Sie die Ergebnismengen in beide Richtungen. Eine Abfrage, die zehnmal schneller ist und ein anderes Ergebnis liefert, ist keine Optimierung, sondern ein Fehler mit gutem Zeitverhalten. Der erste Punkt ist genau die Falle, die ich eben angedeutet habe. Die Unterabfrage liefert für eine Buchung ohne Posten einen leeren Wert — der Verbund lässt diese Buchung dagegen ganz verschwinden.
23:33 Das Ergebnis ist plötzlich kürzer, und in einer Auswertung fällt das leicht erst Wochen später auf. Wer das im Hinterkopf hat, prüft nach dem Umbau nicht nur die Zeit, sondern auch die Zeilenzahl.
Wie Tabellenstatistiken den Planner steuern
23:45 Ein Kapitel, das erklärt, warum eine Abfrage über Nacht langsam werden kann, ohne dass jemand etwas geändert hätte. Kein neuer Code, kein neuer Index, kein Umzug — und trotzdem ein völlig anderer Plan. Die Erklärung liegt in den Statistiken, mit denen der Planner rechnet. Wir sehen uns an, woran man erkennt, dass sie veraltet sind, und wie man das mit einem einzigen Kommando wieder geradezieht.
24:09 Wir hatten es schon: Der Planner probiert nicht aus, er rechnet. Und um zu rechnen, braucht er ein Bild Ihrer Daten — wie viele Zeilen, wie verteilt sich ein Wert, wie selektiv ist eine Bedingung. Dieses Bild ist eine Momentaufnahme. Ändern sich die Daten stark, veraltet es. Und dann trifft der Planner völlig vernünftige Entscheidungen auf falscher Grundlage.
24:31 Das ist wichtig zu verstehen: Er macht keinen Fehler. Er hat nur veraltete Informationen. Der zweite Schritt ist der Test, den Sie sich merken sollten: Größenordnungen. Wenn der Planner sechsundzwanzigtausend Zeilen erwartet und zweihundertvierzehntausend findet, ist das keine Ungenauigkeit, das ist ein anderes Universum.
24:50 Bei so einer Abweichung brauchen Sie keinen neuen Index — Sie brauchen frische Statistiken. Und der letzte Schritt ist wieder derselbe wie überall in diesem Modul: erneut messen. Ohne den zweiten Plan wissen Sie nicht, ob Sie das Richtige getan haben. Die interessanteste Spalte in dieser Abfrage ist die letzte: Sie zählt die Änderungen seit der letzten Auffrischung.
25:11 Eine große Zahl dort sagt Ihnen sofort, dass das Bild des Planners nicht mehr stimmt. Und zum Kommando selbst eine beruhigende Anmerkung: Es ändert keine Daten und sperrt nichts nennenswert. Es sieht sich eine Stichprobe an und schreibt das Ergebnis in den Katalog. Sie können es also ohne großes Zeremoniell ausführen, wenn der Verdacht besteht.
25:32 Im Normalfall erledigt das die Automatik, und Sie müssen sich nicht darum kümmern. Die vier Fälle hier sind die Ausnahmen — und ihnen ist eines gemeinsam: Die Daten ändern sich schlagartig, während die Automatik auf einen Schwellwert wartet. Der letzte Fall ist der, den man in Projekten am häufigsten übersieht. Direkt nach einer Migration steht die Tabelle voll, aber der Planner weiß noch nichts davon — und die erste Auswertung darauf ist entsprechend enttäuschend.
25:58 Der erste Punkt ist der Reflex, den dieses Kapitel korrigieren soll: Auf eine Schätzabweichung mit einem weiteren Index zu antworten, ist die häufigste Fehldiagnose überhaupt. Sie bauen dann etwas, das das Problem nicht berührt. Und der dritte Punkt räumt eine verbreitete Verwechslung aus: Das Aufräumen und das Auffrischen der Statistik sind zwei verschiedene Aufgaben.
26:19 Sie werden oft gemeinsam erledigt, aber sie sind nicht dasselbe.
Blockierende Sitzungen erkennen
26:23 Zum Schluss ein Fall, der wie ein Performance-Problem aussieht und keines ist. Die Abfrage ist harmlos, der Server ist entspannt, und trotzdem passiert nichts. Wer diese Kombination einmal erlebt hat, erkennt sie danach sofort — und spart sich eine halbe Stunde, in der er Abfragen optimiert, die gar nicht das Problem sind.
26:42 Wir sehen uns an, wie man den Verursacher in einer einzigen Abfrage findet. Sperren sind völlig normal und meist so kurzlebig, dass niemand sie bemerkt. Zum Vorfall werden sie erst, wenn eine Sitzung eine Sperre hält, die eine andere braucht. Und der häufigste Fall im Betrieb ist besonders unauffällig: eine Sitzung, die eine Transaktion geöffnet, etwas geändert und dann nichts mehr getan hat.
27:05 Sie sieht im Werkzeug völlig untätig aus. Sie hält trotzdem alle ihre Sperren — wie jemand, der die Umkleidekabine abgeschlossen hat und dann telefonieren gegangen ist. Diese Abfrage macht in einem Schritt das, wofür man sonst durch mehrere Sichten klickt: Sie zeigt, wer wartet und auf wen. Die entscheidende Angabe ist der Zustand des Blockierers.
27:26 Steht dort, dass er innerhalb einer offenen Transaktion untätig ist, dann fehlt dem System keine Rechenleistung — es fehlt ein COMMIT. Diese Unterscheidung ändert alles, was Sie danach tun. Und das Transaktionsalter sagt Ihnen, wie dringend es ist. Die Tücke liegt im vierten Punkt. Nach außen wirkt das wie ein allgemein langsamer Server: Mehrere Anfragen hängen, die Anwendung reagiert träge, Beschwerden treffen ein.
27:52 Und wenn Sie dann in die Auslastung sehen, ist der Server ruhig. Genau diese Kombination — träge Anwendung, entspannter Server — ist das Erkennungszeichen. Wer sie kennt, sucht sofort an der richtigen Stelle statt eine halbe Stunde Abfragen zu optimieren, die gar nicht das Problem sind. Diese Aufgabe sollten Sie wirklich einmal von Hand durchspielen, mit zwei oder drei Konsolen nebeneinander.
28:15 Der Moment, in dem die wartende Anweisung nach dem COMMIT einfach durchläuft, sagt mehr als jede Erklärung. Und die Ausbaustufe ist zugleich eine sehr praktische Empfehlung für den Betrieb: Es gibt eine Einstellung, die Sitzungen mit offener, untätiger Transaktion nach einer Weile selbst beendet. Ein einfaches Mittel gegen eine sehr häufige Ursache.
28:35 Die ersten beiden Punkte gehören zusammen und beschreiben den typischen Griff zum falschen Hebel. In der Hektik beendet man die Sitzung, die hängt — und damit das Opfer statt des Verursachers. Und die härteste Maßnahme ist selten die nötige: Ein Abbruch der laufenden Anweisung reicht oft, und ein COMMIT an der richtigen Stelle reicht fast immer.
28:55 Wer hier eine Stufe früher ansetzt, verliert weniger.
Vom Plan zur Maßnahme
28:58 Fassen wir zusammen. Jede Optimierung beginnt mit einem gemessenen Plan und endet mit einem zweiten — dazwischen liegt genau eine Änderung. Indizes helfen, wo sie ein echtes Zugriffsmuster tragen, und kosten überall sonst. Und die größte Wirkung hatte in diesem Modul nicht ein Index, sondern eine umgeschriebene Abfrage. Im nächsten Modul verlassen wir die einzelne Abfrage und sehen uns den Dauerbetrieb an: Wie erkennt man, welche Anweisungen das System wirklich belasten, und wie hält man ein System über Monate gesund?
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →