Arbeit an der Datenbank Performance beginnt meist damit, dass jemand eine größere Instanz vorschlägt, und sie endet meist mit der Entdeckung, dass eine einzige Abfrage bei jedem Seitenaufruf einen sequenziellen Scan über 4 Millionen Zeilen gefahren hat. Die Hardware war nie die Beschränkung. Der Ausführungsplan war es.
Dieses Muster ist konsistent genug, um es als Standardannahme zu formulieren. Wenn eine Anwendung langsam ist und die Datenbank ausgelastet, liegt die Ursache fast immer in einer kleinen Zahl konkreter Abfragen und nicht in einem allgemeinen Mangel an Kapazität. Ein größerer Server verdeckt das Problem genau so lange, wie die Tabelle braucht, um wieder zu wachsen.
Messen Sie, bevor Sie etwas ändern. Eine Abfrage zu optimieren, die Sie nur vermutet haben, ist der Weg, auf dem Teams eine Woche damit verbringen, Indizes anzulegen, die Schreibvorgänge verlangsamen und Lesevorgänge kein bisschen beschleunigen. Jede Datenbank kann Ihnen sagen, welche Anweisungen die meiste Gesamtzeit verbrauchen. Fangen Sie dort an, beheben Sie die schlimmste, messen Sie erneut. Zwei oder drei Durchläufe davon beenden den Vorfall in der Regel.
Datenbank Performance beginnt damit, die Abfrage zu finden
Die Gesamtzeit zählt mehr als der schlechteste Einzelfall. Eine Abfrage, die zwei Sekunden braucht und zweimal am Tag läuft, ist irrelevant. Eine Abfrage, die vierzig Millisekunden braucht und 8.000 Mal pro Minute läuft, ist Ihr Problem, und sie taucht in einem Slow Query Log mit einer Schwelle von einer Sekunde niemals auf.
In Postgres aggregiert die Erweiterung pg_stat_statements genau das: Aufrufe, Gesamtzeit und mittlere Zeit je normalisierter Anweisung. Sortieren Sie nach Gesamtzeit, und der Übeltäter steht meist unter den ersten drei. MySQL bietet über das Performance Schema eine vergleichbare Aggregation.
Zwei Dinge lohnen eine Prüfung, bevor Sie die Abfrage selbst für schuldig erklären. Ist sie jedes Mal langsam oder nur zu bestimmten Stunden, was eher auf Konkurrenz um Ressourcen als auf den Plan hindeutet? Und ist sie isoliert langsam oder nur unter Parallellast, was auf Sperren oder auf Verbindungsgrenzen hindeutet?
Lesen Sie den Plan, statt zu raten
Sobald Sie die Anweisung haben, fragen Sie die Datenbank, wie sie diese auszuführen gedenkt. Postgres macht das über EXPLAIN sichtbar
, und die wichtige Variante ist EXPLAIN ANALYZE, die die Abfrage tatsächlich ausführt und echte Laufzeiten statt Schätzungen meldet.
Drei Dinge in dieser Ausgabe tragen den größten Teil des Signals.
Sequenzieller Scan auf einer großen Tabelle. Die Datenbank liest jede Zeile. Auf einer kleinen Tabelle ist das korrekt und schnell. Auf einer großen bedeutet es, dass es für die geschriebene Bedingung keinen nutzbaren Index gibt oder dass der Planer den Index als nicht lohnend eingestuft hat.
Eine große Lücke zwischen geschätzter und tatsächlicher Zeilenzahl. Der Planer wählt seine Strategie anhand von Statistiken. Wenn seine Schätzung um Größenordnungen danebenliegt, entscheidet er aus Gründen schlecht, die mit Ihrer Abfrage nichts zu tun haben. Veraltete Statistiken sind eine häufige und leicht behebbare Ursache.
Zeit, die sich in einem einzigen Knoten ballt. Pläne sind Bäume, und die Korrektur gehört an den Knoten, der die Zeit verbraucht hat. Alles andere zu optimieren ändert nichts.
Der Reflex, sofort einen Index anzulegen, sobald ein sequenzieller Scan auftaucht, ist oft richtig und trotzdem dreißig Sekunden Zurückhaltung wert, denn der Grund, warum der Index nicht genutzt wird, wiegt manchmal schwerer als sein Fehlen.
Warum Indizes nicht helfen
Ein Index, der existiert, ist noch lange kein Index, der auch benutzt wird.
Die Bedingung ist nicht sargable. Eine Spalte in eine Funktion zu verpacken oder mit ihr zu rechnen verhindert in der Regel, dass der Index auf dieser Spalte genutzt wird, denn der Index speichert die Spaltenwerte und nicht die transformierten. Die Bedingung so umzuschreiben, dass die Spalte unangetastet stehen bleibt, stellt die Nutzung meist wieder her.
Die Spaltenreihenfolge in einem zusammengesetzten Index stimmt nicht. Ein zusammengesetzter Index unterstützt Abfragen, die seine führenden Spalten verwenden. Ein Index auf erst der einen und dann der anderen Spalte hilft einer Abfrage nicht, die nur auf die zweite filtert, und darauf fallen Teams ständig herein.
Der Planer hält einen Scan für billiger. Wenn eine Abfrage einen großen Anteil der Tabelle zurückgibt, ist sequenzielles Lesen tatsächlich schneller als der Sprung durch einen Index. Das ist korrektes Verhalten, und die Korrektur besteht darin, weniger zurückzugeben.
Die Statistiken sind veraltet. Nach einem Massenimport oder einer großen Löschung kann das Bild, das der Planer von den Daten hat, gründlich falsch sein, bis die Statistiken aufgefrischt werden.
Und jeder Index kostet etwas. Schreibvorgänge müssen ihn pflegen, und er belegt Speicher, der sonst Daten cachen würde. Eine Tabelle mit fünfzehn Indizes hat meist mehrere, die niemand braucht, und jeder davon macht jedes Insert langsamer.
Das N+1 Problem ist weiterhin die größte Einzelursache
Mehr Langsamkeit in Anwendungen stammt von hier als von jedem Planproblem, und sie zeigt sich nie als langsame Abfrage, weil jede einzelne Abfrage schnell ist.
Die Form ist vertraut. Holen Sie eine Liste von 100 Datensätzen, iterieren Sie darüber und holen Sie für jeden Eintrag die zugehörigen Daten. Das Ergebnis sind 101 Roundtrips, wo einer oder zwei genügt hätten. Jede Abfrage kommt in drei Millisekunden zurück, und die Seite braucht trotzdem eine halbe Sekunde, weil die Kosten in den Roundtrips liegen und nicht in der Arbeit.
Objektrelationale Mapper machen es leicht, das versehentlich zu schreiben, denn der Zugriff auf die verknüpften Daten sieht aus wie ein Zugriff auf eine Eigenschaft und nicht wie ein Datenbankaufruf. Die Korrektur besteht darin, die verknüpften Daten in einer Abfrage zusammen mit der Elternmenge zu laden, was jedes ausgereifte ORM unterstützt und was die meisten standardmäßig nicht tun.
Die Erkennung ist unkompliziert: zählen Sie die Abfragen pro Request. Wenn eine Seite eine Anzahl von Abfragen absetzt, die proportional zur Anzahl der angezeigten Elemente ist, haben Sie es gefunden. Das ist außerdem das Lohnendste, was Sie prüfen können, wenn eine Anwendung an der Edge langsam ist, wie unser Leitfaden zu Cloudflare Hyperdrive beschreibt, denn Roundtrips kosten deutlich mehr, wenn die Distanz größer ist.
Verbindungen und Konkurrenz
Zwei Probleme, die wie Langsamkeit aussehen und keine sind.
Erschöpfte Verbindungen. Jede Datenbank hat eine Obergrenze für gleichzeitige Verbindungen, und jede einzelne kostet Speicher. Wenn eine Anwendung mehr öffnet, als der Pool zulässt, stehen Requests in der Warteschlange und warten auf eine Verbindung, und die Anwendung wirkt langsam, während die Datenbank untätig dasteht. Das Symptom ist hohe Anwendungslatenz bei niedriger Datenbank-CPU, und die Korrektur heißt Pooling und nicht größere Maschine.
Sperrkonkurrenz. Eine lange Transaktion, die eine Sperre hält, blockiert alles, was dahinter wartet. Die übliche Ursache ist eine Transaktion, die über Arbeit hinweg offen bleibt, die die Datenbank gar nicht braucht, etwa ein HTTP-Aufruf an einen anderen Dienst. Halten Sie Transaktionen kurz und auf die Datenbankarbeit selbst beschränkt.
Beide Fälle lohnt es sich früh auszuschließen, denn beide lassen sich leicht als Abfrageproblem missdeuten und keiner von beiden wird durch einen Index behoben.
Was in welcher Reihenfolge zu tun ist
Finden Sie die Anweisungen, die die meiste Gesamtzeit verbrauchen. Führen Sie EXPLAIN ANALYZE auf der schlimmsten aus und lesen Sie, wohin die Zeit tatsächlich geht. Prüfen Sie die Abfragezahl pro Request, um N+1 auszuschließen, bevor Sie irgendetwas optimieren. Frischen Sie die Statistiken auf, bevor Sie einen Index anlegen, denn manchmal ist das bereits die ganze Lösung. Legen Sie dann den engsten Index an, der die Bedingung bedient, und messen Sie erneut.
Mecanik macht das als Teil unserer Arbeit in der Softwareentwicklung , und das Ergebnis ist fast immer dasselbe: zwei oder drei Abfragen waren verantwortlich, die Korrektur war klein, und die größere Instanz, die niemand gekauft hatte, wurde nie gebraucht.
Siehe auch: API-Versionierung: wann man brechen darf und wie nicht , Wie man 2026 eine Web-App entwickelt , Passwörter speichern: was Sie 2026 verwenden sollten , Individuelle Softwareentwicklung UK .
Häufig gestellte Fragen
Wie finde ich heraus, welche Abfrage meine Anwendung ausbremst? Sortieren Sie nach Gesamtzeit statt nach dem schlechtesten Einzelfall. Eine Abfrage von vierzig Millisekunden, die 8.000 Mal pro Minute läuft, kostet weit mehr als eine Abfrage von zwei Sekunden, die zweimal am Tag läuft, und sie taucht in einem Slow Query Log mit einer Schwelle von einer Sekunde niemals auf. In Postgres aggregiert pg_stat_statements Aufrufe und Gesamtzeit je Anweisung, MySQL bietet dasselbe über das Performance Schema.
Worauf sollte ich in der Ausgabe von EXPLAIN ANALYZE achten? Drei Dinge tragen den größten Teil des Signals: ein sequenzieller Scan auf einer großen Tabelle, also kein nutzbarer Index; eine große Lücke zwischen geschätzter und tatsächlicher Zeilenzahl, also ein Planer, der mit schlechten Statistiken arbeitet; und Zeit, die sich in einem Knoten des Planbaums ballt, denn genau dorthin gehört die Korrektur. Jeden anderen Knoten zu optimieren ändert nichts.
Warum wird mein Index nicht genutzt? Meist aus einem von vier Gründen. Die Bedingung verpackt die Spalte in eine Funktion oder rechnet mit ihr, sodass der Index nicht mehr passt. Der zusammengesetzte Index hat seine Spalten in einer Reihenfolge, die die Abfrage nicht unterstützt. Die Abfrage gibt einen so großen Anteil der Tabelle zurück, dass ein Scan tatsächlich billiger ist. Oder die Statistiken sind nach einem Massenimport oder einer Löschung veraltet.
Was ist ein N+1 Abfrageproblem? Eine Liste von Datensätzen zu holen und dann für jeden einzelnen eine separate Abfrage für die zugehörigen Daten abzusetzen, was 101 Roundtrips erzeugt, wo einer oder zwei genügt hätten. Es taucht in keinem Slow Query Log auf, weil jede Abfrage schnell ist; die Kosten liegen in den Roundtrips. Erkennen lässt es sich, indem Sie die Abfragen pro Request zählen und auf eine Anzahl achten, die proportional zu den angezeigten Elementen ist.
Behebt ein größerer Datenbankserver langsame Abfragen? Selten, und nur vorübergehend. Wenn eine Anwendung langsam ist und die Datenbank ausgelastet, liegt die Ursache fast immer in einer kleinen Zahl konkreter Abfragen und nicht in fehlender Kapazität, sodass mehr Hardware das Problem nur verdeckt, bis die Tabelle wieder wächst. Hohe Anwendungslatenz bei niedriger Datenbank-CPU deutet stattdessen meist auf einen erschöpften Verbindungspool hin, und dagegen hilft mehr Hardware nicht.
Kommentare