← Datenbankadministration für Sysadmins
Datenbankzustand beobachten: Momentaufnahme, Zähler und Wartevorgänge
In der Sitzungsübersicht steht ein DELETE auf eine große Fachtabelle. Daneben steht idle. Wer die Sitzung jetzt beendet, beendet eine, die nichts tut — denn die Spalte zeigt in diesem Zustand die zuletzt gelaufene Abfrage. Dieses Modul liest die Bordinstrumente beider Systeme so, wie ihre Dokumentation sie meint: Momentaufnahme gegen aufgelaufenen Zähler, Warten statt Auslastung, und die Felder, die stillschweigend leer bleiben, wenn das Überwachungskonto zu wenig darf.
Lehrtext · 10 Abschnitte · zuletzt geprüft: 2026-09-12
Ein Wert ist keine Diagnose
„Die Datenbank ist langsam“ und „die CPU steht bei 95 Prozent“ sind zwei Aussagen, und nur die zweite ist gemessen. Ob sie zusammengehören, ist eine eigene Frage — eine ausgelastete CPU kann der Grund für die Langsamkeit sein, ihre Folge, oder völlig unbeteiligt.
Datenbanken machen das besonders sichtbar, weil sie fast nie an einer einzelnen Ressource hängen. Eine Abfrage, die zehn Sekunden braucht, hat davon vielleicht neun gewartet: auf eine Sperre, auf eine Platte, auf das Protokoll. In dieser Zeit ist nichts ausgelastet, und trotzdem passiert nichts.
Zwei Sichten, die ständig verwechselt werden
Bevor irgendeine Zahl gelesen wird, gehört geklärt, welcher Art sie ist. Die Dokumentation trennt die beiden ausdrücklich und hält fest, dass die eine Einrichtung von der anderen unabhängig ist.
Momentaufnahme — Was gerade jetzt passiert: welche Sitzungen es gibt, was sie tun, worauf sie warten. In PostgreSQL pg_stat_activity, bei SQL Server sys.dm_exec_requests und sys.dm_exec_sessions.
Kumulativer Zähler — Was sich seit einem Startpunkt aufsummiert hat: gelesene Blöcke, Wartezeiten, Vorgänge. In PostgreSQL die pg_stat_*-Sichten, bei SQL Server unter anderem sys.dm_os_wait_stats.
Die Unterscheidung ist nicht akademisch, weil die beiden verschiedene Fragen beantworten. Eine Momentaufnahme beantwortet „wer blockiert gerade wen“. Ein kumulativer Zähler beantwortet „worauf ist über einen Zeitraum am meisten Zeit gegangen“. Wer den Zähler für die erste Frage nimmt, bekommt einen Mittelwert über Wochen, in dem der aktuelle Vorfall untergeht.
track_activities schaltet die Beobachtung des gerade ausgeführten Befehls ein, track_counts die kumulativen Zähler über Tabellen- und Indexzugriffe, track_io_timing die Messung der Zeiten für Lesen, Schreiben, Erweitern und Synchronisieren von Blöcken. Wer eine Sicht auswertet, deren Grundlage abgeschaltet ist, bekommt keine Fehlermeldung, sondern Nullen — und ohne track_io_timing fehlt genau die Größe, mit der sich Wartezeit auf Speicher überhaupt beziffern ließe.Der Bezugszeitraum, den niemand nennt
Ein kumulativer Zähler ohne Angabe, seit wann er zählt, ist keine Kennzahl, sondern eine Zahl. Beide Systeme setzen diese Zähler unter Umständen zurück, ohne dass jemand etwas tut.
Bei SQL Server ist das ausdrücklich dokumentiert: Die Wartestatistiken werden über einen Neustart der Engine nicht hinweg aufbewahrt, und alle Daten sind kumulativ seit dem letzten Zurücksetzen oder dem Start der Engine. Zurücksetzen lässt sich die Sicht mit DBCC SQLPERF; den letzten Startzeitpunkt liefert die Spalte sqlserver_start_time in sys.dm_os_sys_info. Ohne diesen Bezug ist die größte Wartezahl bedeutungslos.
Praktisch heißt das: Jede Auswertung kumulativer Werte beginnt damit, den Startpunkt mit auszulesen. In PostgreSQL führen die Statistiksichten dafür eine eigene Spalte mit dem Zeitpunkt des letzten Zurücksetzens. Ohne diese Spalte im Bericht vergleicht man später Zeiträume verschiedener Länge und hält das Ergebnis für eine Veränderung.
Die Spalte, die falsch gelesen fast immer zum falschen Eingriff führt
In pg_stat_activity steht zu jeder Sitzung eine Spalte query. Der naheliegende Schluss — hier steht, was diese Sitzung gerade tut — ist nur in einem von mehreren Fällen richtig.
state gleich active, zeigt das Feld die gerade laufende Abfrage. In allen anderen Zuständen zeigt es die zuletzt ausgeführte Abfrage.Eine Sitzung im Zustand idle zeigt also weiterhin die letzte Abfrage an, die sie ausgeführt hat — vor drei Sekunden oder vor drei Stunden. Wer eine Übersicht nach query filtert und dort ein bedrohlich aussehendes DELETE findet, hat damit nichts über eine laufende Operation belegt. Die Sitzung tut in diesem Moment nichts.
active- Führt gerade eine Abfrage aus. Nur hier beschreibt
queryetwas Laufendes. idle- Verbunden, ohne offene Transaktion, tut nichts.
queryist Vergangenheit. idle in transaction- Transaktion offen, aber keine Abfrage aktiv. Der interessante Fall — die Sperren dieser Transaktion bestehen weiter.
idle in transaction (aborted)- Wie oben, aber eine Anweisung der Transaktion hat einen Fehler geworfen. Es kommt nur noch ein Zurückrollen infrage.
disabled- Wird gemeldet, wenn
track_activitiesfür dieses Backend abgeschaltet ist. Dann gibt es überhaupt keine Aussage.
idle in transaction ist die Zeile, nach der man sucht — nicht active. Eine aktive Abfrage arbeitet; eine offene, untätige Transaktion hält ihre Sperren und blockiert andere, ohne selbst Fortschritt zu machen. Genau das ist der Zustand, den ein Dialogfenster erzeugt, das auf eine Benutzereingabe wartet.track_activity_query_size. Wer eine lange, generierte Abfrage anhand dieser Spalte wiederzuerkennen versucht, vergleicht möglicherweise nur deren identischen Anfang.Wer was überhaupt sehen darf
Ein Überwachungskonto wird üblicherweise mit möglichst wenig Rechten angelegt. Bei den Statistiksichten führt genau das zu einem Fehlerbild, das aussieht wie ein ruhiges System.
pg_read_all_stats sehen alles.Die Folge ist tückisch, weil nichts fehlschlägt. Die Abfrage läuft, liefert Zeilen, und in den Spalten state, query und wait_event steht überall nichts. Ein Dashboard, das daraus „keine aktiven Abfragen“ ableitet, meldet Ruhe, während eine Sperrkette den Dienst anhält. Das ist kein Rechtefehler mit Meldung, sondern ein stiller Sichtbarkeitsfehler.
Kurzcheck
Ein Überwachungskonto meldet dauerhaft null aktive Abfragen, obwohl Anwender von Hängern berichten. Was prüfst du zuerst?
- Ob das Konto die Rechte hat, fremde Sitzungen überhaupt zu sehen
- Ob die Abfrage des Dashboards einen Syntaxfehler enthält
- Ob die Anwender sich auf eine andere Instanz verbinden
Treffer. Richtig. Ohne die entsprechenden Rechte liefert die Sicht Zeilen mit lauter Nullwerten in genau den Spalten, die das Dashboard auswertet — ohne Fehlermeldung.
Statistiken frieren in einer Transaktion ein
Eine zweite Eigenheit trifft jeden, der Statistiken in einem Skript ausliest statt in einer einzelnen Abfrage.
Fragt ein Serverprozess aufgelaufene Statistiken ab, werden die abgerufenen Werte laut Dokumentation in der Voreinstellung bis zum Ende der aktuellen Transaktion zwischengespeichert. Solange die Transaktion läuft, zeigen die Sichten damit unveränderte Zahlen. Dasselbe gilt für die Angaben zu den aktuellen Abfragen aller Sitzungen: Sie werden beim ersten Zugriff innerhalb der Transaktion erhoben und danach unverändert weitergereicht.
Das Skript, das immer dasselbe Bild liefert
Szenario
Ein Diagnoseskript soll über zwei Minuten hinweg alle fünf Sekunden die aktiven Sitzungen erfassen, um eine Sperrkette einzufangen. Es liefert 24 identische Momentaufnahmen.
Anforderungen
- Alle Messungen laufen über dieselbe Verbindung
- Das Skript öffnet zu Beginn eine Transaktion und schließt sie am Ende
- Zwischen den Messungen wird nichts bestätigt
Schritte
- Die erste Messung baut den Schnappschuss auf. Alle folgenden Messungen innerhalb derselben Transaktion bekommen genau diesen Schnappschuss erneut geliefert.
- Der einfachste Weg ist, jede Messung in einer eigenen Transaktion laufen zu lassen — im Autocommit also ohne umschließendes
BEGIN. - Muss das Skript aus anderen Gründen in einer Transaktion bleiben, verwirft
pg_stat_clear_snapshot()den zwischengespeicherten Stand, sodass der nächste Zugriff neu erhebt. - Alternativ steuert
stats_fetch_consistencydas Verhalten grundsätzlich: Der Wertnoneverzichtet auf das Zwischenspeichern, wenn ohnehin nur einmal zugegriffen wird.
Merksatz: Eine Messreihe, die sich nicht verändert, ist zuerst ein Verdacht gegen die Messung — nicht ein Befund über das System.
Warten messen statt Auslastung
Die nützlichste Sicht in beiden Systemen ist die auf Wartevorgänge. Sie beantwortet nicht, was beschäftigt war, sondern wodurch Arbeit aufgehalten wurde — und das ist fast immer die Frage, die hinter einer Beschwerde steht.
PostgreSQL führt dazu in pg_stat_activity zwei Spalten: wait_event_type und wait_event. Beide sind null, wenn das Backend gerade auf nichts wartet. Eine Abfrage auf die Zeilen mit gesetztem Wartetyp liefert unmittelbar die Liste dessen, was aufgehalten wird, und woran. Ein Wartetyp Lock etwa verweist auf eine Sperre — also auf eine andere Sitzung, nicht auf ein Gerät.
SQL Server führt die aufgelaufene Variante in sys.dm_os_wait_stats, nach Wartetyp aufsummiert. Für den aktuellen Moment stehen die Spalten wait_type und wait_time in sys.dm_exec_requests — mit einer wichtigen Einschränkung: Sie sind laut Dokumentation nur gefüllt, wenn die Anforderung gerade blockiert ist. Was zuletzt der Grund war, steht getrennt in last_wait_type.
relation, WALWriteLock oder DataFileRead, und keiner dieser Namen erklärt sich selbst. PostgreSQL führt dazu die Systemsicht pg_wait_events mit Typ, Name und einer Beschreibung je Ereignis. Die Dokumentation zeigt ausdrücklich, wie man sie über Typ und Name an pg_stat_activity anfügt — damit wird aus einer Abkürzung ein Satz, den man in ein Ticket schreiben kann.sys.dm_os_waiting_tasks.`blocking_session_id` und seine Sonderwerte
Die Spalte blocking_session_id in sys.dm_exec_requests ist der kürzeste Weg zu einer Blockierungskette — und eine Einladung zu einer Alarmregel, die falsch auslöst.
Laut Dokumentation bedeutet null oder 0, dass die Anforderung nicht blockiert ist — oder dass die Angaben zur blockierenden Sitzung nicht verfügbar sind beziehungsweise sich nicht ermitteln lassen. Beides fällt auf denselben Wert zusammen; „nicht blockiert“ und „nicht feststellbar“ sind hier nicht unterscheidbar.
Dazu kommen negative Sonderwerte mit eigener Bedeutung. Die Dokumentation hebt einen davon hervor: Der Wert `-5` bedeutet, dass sich die Sitzung des Sperrbesitzers für diesen Sperrtyp nicht nachverfolgen lässt — und sie stellt ausdrücklich fest, dass -5 für sich genommen kein Leistungsproblem anzeigt. Es weist darauf hin, dass die Sitzung auf den Abschluss eines asynchronen Vorgangs wartet. Je nach Last kann dieser Wert häufig auftreten.
blocking_session_id ungleich 0 ist“ feuert damit dauerhaft auf einem gesunden System. Wer den Alarm daraufhin stummschaltet, verliert auch die echten Blockierungen. Die Regel muss die negativen Sonderwerte ausnehmen — und zusätzlich eine Mindestdauer verlangen, weil kurzes Blockieren normaler Betrieb ist.Das Protokoll als Messinstrument
Was die Momentaufnahme nicht einfängt, weil es zwischen zwei Messungen passiert, fängt das Serverprotokoll ein — vorausgesetzt, es ist dafür eingestellt. Die Voreinstellungen sind hier bewusst zurückhaltend.
log_min_duration_statement- Protokolliert die Dauer jeder Anweisung, die mindestens so lange lief. Voreinstellung ist
-1— abgeschaltet. Der Wert0protokolliert alle Anweisungen. log_min_duration_sample- Dasselbe, aber nur für einen Teil der Anweisungen; der Anteil steht in
log_statement_sample_rate. Gedacht für Lasten, bei denen alles zu protokollieren zu viel wäre. log_lock_waits- Erzeugt einen Eintrag, wenn eine Sitzung länger als
deadlock_timeoutauf eine Sperre wartet. Voreinstellung aus. log_autovacuum_min_duration- Protokolliert Aufräumvorgänge ab dieser Dauer; Voreinstellung 10 Minuten. Meldet zusätzlich, wenn ein Vorgang wegen einer konfliktierenden Sperre übersprungen wurde.
log_checkpoints- Protokolliert Prüfpunkte samt Kennzahlen. Voreinstellung an.
log_min_duration_statement = 0 protokolliert jede einzelne Anweisung mit ihrem vollen Text. Auf einer Instanz mit nennenswerter Last erzeugt das ein Protokollvolumen, das das Dateisystem füllt und die Schreiblast spürbar erhöht — also genau die Wirkung, die man untersuchen wollte. Der sinnvolle Einstieg ist eine Schwelle oberhalb dessen, was normal ist; alles zu protokollieren ist eine Maßnahme für eine Testinstanz.log_error_verbosity steuert, wie ausführlich eine Meldung im Serverprotokoll steht. Der Wert VERBOSE nimmt laut Dokumentation den SQLSTATE-Fehlercode mit auf. Das ist der Unterschied zwischen einer Protokollzeile, die man lesen muss, und einer, die sich zählen lässt: Serialisierungsfehler tragen immer den Code 40001, und erst mit dem Code im Protokoll lässt sich beantworten, ob ihre Zahl nach einer Umstellung gestiegen ist.Ein nützlicher Feinunterschied betrifft log_duration: Die Dokumentation hält fest, dass eine überschrittene Schwelle bei log_min_duration_statement den Abfragetext mitprotokolliert, während log_duration das nicht tut. Beides zusammen ergibt darum ein brauchbares Muster für hoch belastete Installationen: alle Dauern erfassen, den Text nur oberhalb der Schwelle.
Vom Signal zur Hypothese
Der erste Schritt wird am häufigsten übersprungen und spart am meisten Arbeit. Eine hohe Kennzahl ohne betroffenen Anwender ist kein Vorfall, sondern ein Messwert — und die Schwelle, die ihn ausgelöst hat, gehört dann überprüft statt das System.
monitoring-incident-operations. Hier ging es um die Instrumente der Datenbank selbst — und darum, was sie tatsächlich sagen.Jetzt anwenden
Diesen Stoff gibt es als Modul mit bewerteten Entscheidungs-Checks — dieselbe Einführung, danach die Übungen.
Zum Modul →