Wait Events und Wartestatistiken analysieren
AI generated
SELECT
JOIN
SQL Debugging · Wait Events · Root Cause Analyse
Wait Events und Wartestatistiken analysieren
wenn CPU-Auslastung nicht erklärt wo die Zeit bleibt

Eine Datenbank mit niedriger CPU-Auslastung kann trotzdem drastisch langsam antworten, weil Sessions in Wirklichkeit auf Locks, Festplatten-IO oder Netzwerk warten, nicht auf freie Rechenzeit. Wait Events machen genau diese unsichtbare Wartezeit messbar und kategorisierbar. Wer systematisch zwischen Lock Waits und IO Waits unterscheidet, findet die tatsächliche Root Cause, statt an CPU- oder Speichermetriken vorbeizuoptimieren.

18 Min. Lesezeit pg_stat_activity · wait_event · Lock Waits · IO Waits PostgreSQL · MySQL · SQL Server

1. Warum CPU-Zeit allein nicht erklärt, wo die Zeit bleibt

Ein klassischer Diagnosefehler ist, die CPU-Auslastung des Datenbankservers als primären Indikator für Performance-Probleme zu betrachten. Eine Datenbank kann bei zehn Prozent CPU-Auslastung trotzdem drastisch langsam antworten, weil die meisten Sessions in Wirklichkeit gar nicht rechnen, sondern warten: auf eine Sperre, die eine andere Transaktion hält, auf eine Festplattenoperation, die noch nicht abgeschlossen ist, oder auf eine Netzwerkantwort eines Replikationspartners. Aus Sicht von CPU- und Speichermetriken sieht das System entspannt aus, obwohl Anfragen in Wirklichkeit über Sekunden hängen.

Wait Events schließen genau diese Lücke, indem sie für jede Session festhalten, worauf sie aktuell wartet und wie lange bereits. Statt nur zu wissen, dass eine Abfrage langsam ist, zeigt ein Wait Event die konkrete Kategorie der Wartezeit an, etwa Lock-Wartezeit, IO-Wartezeit oder Wartezeit auf einen internen Lightweight-Lock. Diese Kategorisierung verwandelt eine vage Beobachtung, das System ist langsam, in eine konkrete, umsetzbare Diagnose, welche Ressource den Engpass verursacht.

Dieser Perspektivwechsel, von aggregierten Systemmetriken hin zu einer sessionbasierten Sicht auf Wartezeit, ist der eigentliche Kern professioneller Datenbankdiagnose. Klassische Metriken wie CPU-Auslastung, freier Arbeitsspeicher oder IOPS beschreiben den Zustand des gesamten Servers, sagen aber nichts darüber aus, welche einzelne Abfrage oder welcher einzelne Anwendungsfall für einen konkreten Nutzer gerade langsam ist. Wait Events schließen genau diese Lücke zwischen Server-Metrik und Nutzer-Erfahrung, indem sie die Wartezeit bis auf die einzelne Session und den einzelnen Moment herunterbrechen.

2. Was Wait Events konkret messen

Ein Wait Event ist ein Zeitstempel und eine Kategorisierung, die eine Datenbank-Engine für jeden Prozess führt, sobald dieser nicht aktiv Rechenzeit verbraucht, sondern auf eine externe Bedingung wartet. In PostgreSQL wird dieser Zustand über die Spalten wait_event_type und wait_event in pg_stat_activity sichtbar, wobei wait_event_type die grobe Kategorie angibt, etwa Lock, IO oder LWLock, und wait_event das konkrete Ereignis innerhalb dieser Kategorie benennt. Ist wait_event hingegen NULL, befindet sich die Session aktuell im Zustand aktiver Ausführung und wartet auf nichts Externes, ein wichtiger Unterschied, den man beim Filtern von Abfragen gegen diese Sicht immer berücksichtigen sollte.

SQL Server verfolgt ein konzeptionell ähnliches, aber granulareres Modell über sys.dm_os_wait_stats, das kumulative Wartezeiten pro Wait-Typ über alle Sessions seit dem letzten Neustart oder Reset sammelt. Oracle nutzt mit dem Active Session History (ASH) Mechanismus einen samplingbasierten Ansatz, der jede Sekunde den Zustand aller aktiven Sessions aufzeichnet, inklusive des jeweiligen Wait Events. Alle drei Ansätze verfolgen dasselbe Ziel: sichtbar machen, wofür die Zeit einer Session tatsächlich verwendet wird, statt sich auf aggregierte Systemmetriken zu verlassen.

3. Wichtige Wait-Event-Kategorien im Überblick

Für die praktische Diagnose reicht es, sich auf wenige, häufig auftretende Kategorien zu konzentrieren. Lock Waits zeigen an, dass eine Session auf eine Sperre wartet, die eine andere Transaktion hält, klassischerweise durch konkurrierende UPDATE- oder DELETE-Operationen auf denselben Zeilen. IO Waits, in PostgreSQL etwa DataFileRead, zeigen an, dass eine Session auf das physische Lesen von Daten von der Festplatte wartet, oft ein Hinweis auf zu kleinen Buffer-Cache oder fehlende Indizes, die zu übermäßigem Festplattenzugriff führen.

LWLock Waits (Lightweight Locks) in PostgreSQL betreffen interne Synchronisationsmechanismen der Datenbank-Engine selbst, etwa den Zugriff auf gemeinsam genutzte Speicherstrukturen, und treten typischerweise erst bei sehr hoher Parallelität auf. Client Waits zeigen an, dass die Datenbank auf den Client wartet, meist weil die Anwendung Daten langsamer konsumiert, als die Datenbank sie liefern kann, ein Hinweis, der die Ursache aus der Datenbank heraus in die Anwendung verschiebt.

Eine fünfte, seltener beachtete Kategorie sind Wartezeiten auf Replikation, etwa WalSenderWaitForWAL in PostgreSQL, die anzeigen, dass eine Session auf die Bestätigung eines synchronen Replikationspartners wartet, bevor eine Transaktion als abgeschlossen gilt. Diese Kategorie wird oft übersehen, weil sie nur in Konfigurationen mit synchroner Replikation auftritt, kann dort aber die dominante Wartezeit bei Schreiboperationen ausmachen, insbesondere wenn der Replikationspartner über eine langsame oder instabile Netzwerkverbindung angebunden ist.

4. Wartestatistiken live abfragen

Der erste praktische Schritt ist, den aktuellen Zustand aller aktiven Sessions samt ihres Wait Events abzufragen. In PostgreSQL liefert eine gezielte Abfrage gegen pg_stat_activity, gefiltert auf Sessions mit einem gesetzten wait_event, eine Momentaufnahme dessen, worauf das System gerade wartet. Wiederholt man diese Abfrage im Sekundentakt über einen kurzen Zeitraum, entsteht ein einfaches, aber wirksames Sampling-Profil, das die dominanten Wartekategorien sichtbar macht, ganz ohne zusätzliche Monitoring-Software.

In SQL Server liefert sys.dm_os_waiting_tasks denselben Live-Schnappschuss, ergänzt um blocking_session_id, das direkt anzeigt, welche Session die Sperre hält, auf die eine andere Session wartet. Dieses Feld ist besonders wertvoll, weil es den Umweg über einen separaten Lock-Join, wie bei PostgreSQL nötig, überflüssig macht und Blocker und Wartende in einer einzigen Abfrage zusammenführt.


-- PostgreSQL: snapshot of what every active session is currently waiting on
SELECT
    pid,
    usename,
    wait_event_type,
    wait_event,
    now() - query_start AS waiting_since,
    left(query, 60) AS current_query
FROM pg_stat_activity
WHERE wait_event IS NOT NULL
ORDER BY query_start;

-- SQL Server: live view of waiting tasks including the blocking session
SELECT
    wt.session_id,
    wt.wait_type,
    wt.wait_duration_ms,
    wt.blocking_session_id,
    st.text AS current_query
FROM sys.dm_os_waiting_tasks wt
JOIN sys.dm_exec_requests r ON r.session_id = wt.session_id
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) st
ORDER BY wt.wait_duration_ms DESC;

5. Lock Waits von IO Waits unterscheiden

Die Unterscheidung zwischen Lock Waits und IO Waits entscheidet direkt darüber, in welche Richtung weitere Diagnose und Behebung gehen. Ein System mit dominanten Lock Waits leidet unter Konkurrenz zwischen Transaktionen, die Lösung liegt typischerweise in kürzeren Transaktionen, siehe den vorangegangenen Artikel zu lange laufenden Transaktionen, einer geänderten Zugriffsreihenfolge auf Tabellen oder optimistischem statt pessimistischem Locking. Ein System mit dominanten IO Waits hingegen leidet unter physischen Festplattenzugriffen, die Lösung liegt eher in mehr Arbeitsspeicher für den Buffer-Cache, fehlenden Indizes oder schnellerer Storage-Hardware.

Der praktische Fehler, den viele Teams machen, ist, bei jedem Performance-Problem reflexartig mehr Indizes anzulegen, was bei einem primär durch Lock Waits verursachten Problem wirkungslos bleibt oder es sogar verschlimmert, weil zusätzliche Indizes mehr Schreiblast pro Transaktion erzeugen und damit Sperren länger halten. Die Wait-Event-Kategorisierung verhindert diesen Fehlschluss, indem sie vor der Lösung zunächst die tatsächliche Ressource identifiziert, die den Engpass verursacht.

Ein weiterer praktischer Vorteil dieser Trennung zeigt sich bei der Priorisierung von Optimierungsarbeit in Teams mit begrenzter Kapazität. Wenn die Wait-Event-Analyse zeigt, dass ein System zu achtzig Prozent unter Lock Waits leidet, lohnt sich Zeit in Code-Reviews zur Transaktionsgrenzenverkürzung deutlich mehr als Zeit in Hardware-Upgrades für schnelleren Storage, der das eigentliche Problem gar nicht adressiert. Diese datengestützte Priorisierung verhindert, dass Budget in die falsche Richtung fließt, nur weil eine Maßnahme naheliegender oder einfacher umzusetzen erscheint als die andere.


-- PostgreSQL: aggregate wait events by type over a sampling window
-- Run this query every second for a few minutes and sum the counts
SELECT
    wait_event_type,
    wait_event,
    count(*) AS sample_count
FROM pg_stat_activity
WHERE wait_event IS NOT NULL
GROUP BY wait_event_type, wait_event
ORDER BY sample_count DESC;

-- A result dominated by 'Lock' entries points to transaction contention
-- A result dominated by 'IO' entries points to disk or cache pressure

6. Historische Wartedaten sammeln und auswerten

Ein einzelner Live-Schnappschuss zeigt nur den aktuellen Moment, während viele Performance-Probleme intermittierend auftreten und zum Zeitpunkt der manuellen Prüfung bereits wieder verschwunden sind. Deshalb lohnt sich das kontinuierliche Sammeln von Wartestatistiken über die Zeit, etwa durch ein einfaches Cron-Skript, das die Abfrage aus Abschnitt vier im Sekundentakt ausführt und in eine eigene Tabelle schreibt, oder durch etablierte Erweiterungen wie pg_stat_kcache in Kombination mit pg_stat_statements.

SQL Server bietet mit sys.dm_os_wait_stats bereits eine kumulative Sicht seit dem letzten Neustart, was für Trendanalysen nützlich ist, aber keine zeitliche Auflösung einzelner Vorfälle erlaubt. Für diesen Zweck ist Query Store, seit SQL Server 2016 verfügbar, die bessere Wahl, weil er Wartestatistiken pro Query und Zeitintervall speichert und damit rückwirkend zeigt, welche Abfrage zu welchem Zeitpunkt unter welcher Wartekategorie litt, ohne dass man den Vorfall in Echtzeit hätte beobachten müssen.

7. Von Wait Events zur Root Cause Analyse

Wait Events allein liefern eine Kategorisierung, aber noch keine vollständige Erklärung. Der nächste Schritt verbindet die dominante Wartekategorie mit weiterem Kontext: Bei Lock Waits liefert ein Join gegen die Lock-Views, wie im Artikel zu langen Transaktionen gezeigt, die konkret blockierende Session. Bei IO Waits liefert ein Blick auf die Cache-Hit-Ratio des Buffer-Pools, in PostgreSQL berechenbar aus pg_stat_database, den Hinweis, ob zu wenig Arbeitsspeicher für den Working Set der Anwendung zur Verfügung steht.

Erst die Kombination aus Wait-Event-Kategorie, betroffener Tabelle oder Query und zeitlicher Korrelation mit Deployments oder Lastspitzen ergibt eine vollständige Root Cause Analyse. Eine bewährte Vorgehensweise ist, für jeden signifikanten Performance-Vorfall drei Fragen zu beantworten: Welche Wait-Event-Kategorie dominierte, welche konkreten Sessions oder Queries waren beteiligt, und was hat sich zeitlich unmittelbar davor im System oder in einem Deployment geändert.

Diese dreiteilige Fragestruktur eignet sich auch hervorragend als Dokumentationsvorlage für Post-Mortem-Berichte nach einem Performance-Vorfall. Statt einer vagen Beschreibung wie "die Datenbank war langsam" liefert die Beantwortung der drei Fragen einen konkreten, nachvollziehbaren Befund, etwa "neunzig Prozent Lock Waits auf der Tabelle orders, verursacht durch Session 4821, korreliert mit dem Deployment von Version 2.14 um 14:32 Uhr". Ein solcher Befund lässt sich direkt in eine Korrekturmaßnahme übersetzen und dient gleichzeitig als Referenz, falls ein ähnliches Muster erneut auftritt.

8. Typische Wait-Event-Muster und ihre Bedeutung

Bestimmte Muster wiederholen sich in der Praxis so häufig, dass sie sich als Faustregeln etabliert haben. Ein plötzlicher Anstieg von Lock Waits kurz nach einem Deployment deutet meist auf eine neue, länger laufende Transaktion im geänderten Code hin. Ein gleichmäßig hoher Anteil an IO Waits über den ganzen Tag deutet auf einen strukturell zu kleinen Buffer-Cache im Verhältnis zur Datenbankgröße hin, nicht auf ein temporäres Problem.

Ein Anstieg von LWLock Waits speziell bei sehr hoher Nebenläufigkeit, etwa während einer Verkaufsaktion mit vielen gleichzeitigen Verbindungen, deutet auf interne Contention der Datenbank-Engine selbst hin, oft lösbar durch Connection Pooling auf Anwendungsseite, um die Anzahl gleichzeitiger Datenbankverbindungen zu begrenzen. Diese Muster ersetzen keine detaillierte Analyse im Einzelfall, liefern aber einen schnellen ersten Hinweis, in welche Richtung die weitere Untersuchung gehen sollte.

Ein weiteres wiederkehrendes Muster betrifft Client Waits, die im Tagesverlauf regelmäßig zu bestimmten Uhrzeiten ansteigen. Das deutet häufig auf einen Batch-Job oder Report-Export hin, der große Ergebnismengen langsam über eine ungünstig konfigurierte Netzwerkverbindung oder einen unterdimensionierten Anwendungsserver abholt, während die Datenbank selbst bereits fertig gerechnet hat und nur noch auf das Abholen der Daten wartet. Dieses Muster verschiebt die Optimierung bewusst aus der Datenbank heraus in Richtung Netzwerk-Tuning oder Anwendungs-seitiges Batching der Ergebnisverarbeitung.

9. Wait-Event-Typen im Datenbankvergleich

Die konkrete Terminologie und die verfügbaren Werkzeuge zur Wait-Event-Analyse unterscheiden sich zwischen den gängigen Systemen. Die folgende Tabelle stellt die wichtigsten Konzepte gegenüber.

System Live-Sicht Historische Sicht Lock-Kontext direkt verfügbar
PostgreSQL pg_stat_activity Eigenes Sampling nötig Nein, Join gegen pg_locks nötig
SQL Server sys.dm_os_waiting_tasks Query Store Ja, blocking_session_id direkt
Oracle v$session mit wait_class Active Session History (ASH) Teilweise, über blocking_session
MySQL / InnoDB performance_schema.events_waits_current performance_schema History Tables Über sys.innodb_lock_waits

SQL Server und Oracle bieten mit Query Store beziehungsweise Active Session History die ausgereifteren nativen Werkzeuge für historische Wait-Event-Analyse, während PostgreSQL und MySQL hier stärker auf externe Erweiterungen oder eigenes Sampling angewiesen sind, um denselben Detailgrad an rückwirkender Sichtbarkeit zu erreichen.

Für Teams, die mehrere Datenbanksysteme parallel betreiben, empfiehlt sich trotz dieser Unterschiede ein einheitliches begriffliches Modell in der eigenen Dokumentation: Jede Wartekategorie wird unabhängig vom konkreten System als Lock-, IO-, internes Synchronisations- oder Client-Wait klassifiziert, bevor die systemspezifische Abfrage nachgeschlagen wird. Dieses gemeinsame Vokabular erleichtert es Teams, die sowohl PostgreSQL als auch SQL Server betreuen, Diagnoseergebnisse zwischen den Systemen zu vergleichen, ohne bei jedem Wechsel die Terminologie neu zu lernen.

Mironsoft

Wait-Event-Analyse, Root-Cause-Diagnose und Datenbank-Performance

Niedrige CPU-Auslastung, aber trotzdem langsame Abfragen?

Wir analysieren Wait Events und Wartestatistiken, trennen Lock Contention von IO-Engpässen und liefern eine belastbare Root Cause Analyse statt Rätselraten anhand von CPU-Metriken.

Live-Diagnose

Aktuelle Wait Events und blockierende Sessions in Echtzeit auswerten

Historisches Sampling

Kontinuierliche Aufzeichnung von Wartestatistiken für Trendanalysen

Root-Cause-Bericht

Konkrete Ursachen und priorisierte Maßnahmen statt vager Empfehlungen

Bevor die Zusammenfassung den Artikel abschließt, lohnt sich ein kurzer Rückblick auf die gesamte Diagnosekette: von der Beobachtung niedriger CPU-Auslastung trotz spürbarer Langsamkeit über die Live-Abfrage aktueller Wait Events bis zur Unterscheidung zwischen Lock- und IO-dominierten Engpässen und schließlich der historischen Aufzeichnung für wiederkehrende oder intermittierende Vorfälle. Jeder dieser Schritte baut auf dem vorherigen auf und liefert zunehmend konkretere Handlungsoptionen.

Wer diese Kette einmal systematisch durchlaufen hat, etabliert damit ein wiederverwendbares mentales Modell für zukünftige Performance-Vorfälle, unabhängig vom konkreten eingesetzten Datenbanksystem. Die Terminologie mag zwischen PostgreSQL, MySQL, SQL Server und Oracle variieren, das zugrunde liegende Prinzip, Wartezeit kategorisieren statt nur Symptome zu beobachten, bleibt über alle Systeme hinweg identisch und universell anwendbar.

10. Zusammenfassung

Wait Events schließen die Lücke, die reine CPU- und Speichermetriken offen lassen, indem sie sichtbar machen, worauf jede Session konkret wartet, wenn sie nicht aktiv rechnet. Die Unterscheidung zwischen Lock Waits, IO Waits und internen LWLock Waits entscheidet direkt darüber, in welche Richtung Behebungsmaßnahmen gehen sollten, von kürzeren Transaktionen bis zu mehr Buffer-Cache oder besserem Connection Pooling.

Ein einzelner Live-Schnappschuss reicht oft nicht aus, weil viele Probleme intermittierend auftreten, weshalb kontinuierliches Sampling oder native Werkzeuge wie Query Store und Active Session History für eine belastbare Root Cause Analyse unverzichtbar sind. Wer Wait-Event-Kategorie, betroffene Queries und zeitliche Korrelation mit Deployments konsequent zusammenführt, ersetzt Rätselraten durch eine reproduzierbare, datengestützte Diagnose.

Am Ende steht ein einfacher, aber wirksamer Grundsatz: Bevor eine Optimierungsmaßnahme umgesetzt wird, sollte die dominante Wait-Event-Kategorie bekannt sein, nicht nur vermutet. Diese Disziplin verhindert, dass Zeit in Maßnahmen investiert wird, die an der eigentlichen Ursache vorbeigehen, etwa mehr Arbeitsspeicher bei einem tatsächlich durch Lock Contention verursachten Problem, oder umgekehrt.

Als letzter praktischer Hinweis: Wait-Event-Analyse ersetzt nicht die in früheren Artikeln behandelten spezifischen Diagnosetechniken, etwa das Lesen von EXPLAIN-Plänen oder das Aufspüren langer Transaktionen, sondern ergänzt sie um eine übergeordnete, sessionbasierte Perspektive. In der Praxis bewährt sich die Reihenfolge, zunächst über Wait Events die dominante Kategorie zu bestimmen, und erst danach mit dem jeweils passenden, spezialisierten Werkzeug in die Tiefe zu gehen.

Diese Kombination aus breiter Kategorisierung und gezielter Detailanalyse ist letztlich der Grund, warum erfahrene Datenbankadministratoren Wait Events als ersten Anlaufpunkt bei jedem neuen Performance-Vorfall nutzen, unabhängig davon, wie der Vorfall zunächst gemeldet wurde.

Für Einsteiger in dieses Thema empfiehlt sich, mit der einfachsten Live-Abfrage aus Abschnitt vier zu beginnen und sie regelmäßig, auch ohne akuten Vorfall, gegen die eigene Produktionsdatenbank laufen zu lassen. Allein das Beobachten, welche Wait-Event-Kategorien im Normalbetrieb üblich sind, schafft ein intuitives Gefühl dafür, was normal ist und was im Ernstfall als Abweichung auffällt.

Mit wachsender Erfahrung verschiebt sich der Fokus zunehmend von reiner Beobachtung hin zu proaktiver Prävention, etwa durch automatisierte Schwellenwert-Alarme für bestimmte Wait-Event-Kategorien, lange bevor ein Nutzer eine Verlangsamung überhaupt bemerkt.

Genau dieser Übergang von reaktiver Fehlersuche zu proaktivem Monitoring markiert den Unterschied zwischen einem Team, das ständig Brände löscht, und einem Team, das Performance-Probleme erkennt, bevor sie überhaupt zu einem Vorfall werden.

Wait Events und Wartestatistiken analysieren, das Wichtigste auf einen Blick

Kernidee

Wait Events zeigen, worauf eine Session wartet, nicht nur dass sie langsam ist, und machen die Wartezeit kategorisierbar.

Wichtigste Unterscheidung

Lock Waits deuten auf Transaktionskonkurrenz hin, IO Waits auf Festplatten- oder Cache-Engpässe.

Live-Diagnose

pg_stat_activity beziehungsweise sys.dm_os_waiting_tasks liefern den aktuellen Schnappschuss.

Historische Analyse

Kontinuierliches Sampling oder Query Store beziehungsweise Active Session History für zurückliegende Vorfälle.

11. FAQ: Wait Events und Wartestatistiken

1Was ist ein Wait Event?
Eine Kategorisierung, wie eine Session wartet, wenn sie nicht aktiv rechnet, etwa auf eine Sperre oder eine Festplattenoperation.
2Warum reicht CPU-Auslastung nicht?
Eine Datenbank kann bei niedriger CPU-Last trotzdem langsam sein, weil Sessions auf Locks, IO oder Netzwerk warten statt zu rechnen.
3Wie sehe ich Wait Events in PostgreSQL?
Über pg_stat_activity mit wait_event_type und wait_event, gefiltert auf gesetzte Werte.
4Lock Waits vs. IO Waits?
Lock Waits zeigen Wartezeit auf Sperren, IO Waits Wartezeit auf Festplattenzugriff, beide brauchen andere Lösungen.
5Warum lösen mehr Indizes Lock Waits nicht?
Mehr Indizes erhöhen die Schreiblast und können Sperren länger halten, statt das Problem zu lösen.
6Was bietet Query Store?
Wartestatistiken pro Query und Zeitintervall, für rückwirkende Analyse vergangener Vorfälle.
7Was sind LWLock Waits?
Interne Synchronisationsmechanismen der Datenbank-Engine, treten bei sehr hoher Parallelität auf.
8Wie sammle ich historische Wartedaten?
Ein Cron-Skript, das die Abfrage im Sekundentakt ausführt und speichert, da PostgreSQL keine native historische Sicht bietet.
9Was zeigt blocking_session_id?
Direkt, welche Session die Sperre hält, ohne separaten Lock-Join.
10Was bedeutet ein Anstieg nach Deployment?
Meist eine neue, länger laufende Transaktion im geänderten Code, erkennbar an der zeitlichen Korrelation.