SQL Server Blocking: Kaskaden diagnostizieren
Eine Transaktion blockiert dutzende – den Head-Blocker finden und die Wartepyramide gezielt auflösen.
Blocking-Kaskaden auflösen
Eine Transaktion blockiert dutzende: den Head-Blocker finden, die Wartepyramide verstehen — und gezielt statt blind eingreifen.
Es ist Donnerstagnachmittag, 15:33 Uhr. Bei Sparfuchs & Partner steht die Warenwirtschaft — nicht abgestürzt, nicht offline, einfach nur zäh wie Kaugummi. Jede Buchung hängt eine Minute, dann kommt ein Timeout. Der Kollege vom Anwendungsteam hat sp_who2 ausgeführt und meldet aufgeregt: „Da blockieren sich siebenundzwanzig Sessions gegenseitig, der Server ist kaputt!“ Stimmt nicht. Es blockiert genau eine Session — die anderen sechsundzwanzig sind Opfer. Und die eine sitzt seit 14:52 Uhr schlafend auf einer offenen Transaktion, weil ihr Besitzer gerade in einem Meeting ist.
Willkommen bei den Blocking-Kaskaden, dem häufigsten Performance-Drama im SQL-Server-Alltag. Die gute Nachricht: Kaskaden sehen dramatisch aus, haben aber fast immer eine einzige Wurzel — den Head-Blocker. Wer ihn findet, löst die ganze Pyramide mit einem Handgriff auf. Wer ihn nicht findet, killt wahllos Sessions, macht alles schlimmer und steht zwanzig Minuten später wieder am Anfang. Dieser Beitrag aus dem SQL-Server-Notarztwagen (die Triage-Übersicht liegt unter /sql-server-notarztwagen/) zeigt dir die Anatomie der Kette, das Skript für die Ein-Klick-Diagnose und die Entscheidungsregeln für den gezielten Eingriff.
Was bedeutet Blocking technisch?
Sperren sind kein Fehler, sondern das Fundament: SQL Server schützt mit ihnen die Konsistenz paralleler Zugriffe. Eine Session, die Daten ändert, hält eine exklusive Sperre (X) bis zum Ende ihrer Transaktion; eine Session, die liest, nimmt kurzlebige Lesesperren (S). Blocking entsteht, sobald eine Session eine Sperre anfordert, die mit einer bereits vergebenen unvereinbar ist — dann reiht sie sich in die Warteschlange ein. Auf jedem gesunden Server passiert das ununterbrochen, im Millisekundenbereich, und niemand merkt etwas.
Zum Problem wird Blocking durch Dauer: Wenn der Sperren-Inhaber nicht freigibt — weil seine Transaktion lange läuft oder schlicht vergessen wurde —, wachsen die Wartezeiten in den Sekunden- und Minutenbereich. Und weil wartende Sessions ihrerseits Sperren halten können, auf die wieder andere warten, entsteht die Kaskade: eine Wartepyramide, an deren Spitze genau eine Session sitzt, die auf niemanden wartet. Das ist der Head-Blocker — und das einzige Glied der Kette, das dich wirklich interessiert. In den Wait Stats schlägt sich das Ganze als LCK_M_X, LCK_M_S und Verwandte nieder: die Wartetypen-Familie für Sperrkonflikte.
Die Abgrenzung zum Deadlock, weil sie in jeder Eskalation durcheinandergerät: Blocking ist lineares Warten und löst sich auf, sobald der Head-Blocker fertig ist — theoretisch also von selbst. Ein Deadlock ist zirkuläres Warten, löst sich nie von selbst und wird vom Deadlock-Monitor per Error 1205 zwangsaufgelöst. Symptomatisch heißt das: Deadlocks erzeugen Fehlermeldungen, Blocking erzeugt Zähigkeit und Timeouts. Für die 1205er gibt es den Nachbar-Beitrag zu den Deadlock-Wellen — für das zähe Kaugummi bist du hier richtig.
|
Abgrenzung: Blocking vs. Deadlock Blocking — A wartet auf B. Löst sich auf, wenn B committet. Symptom: Langsamkeit, Timeouts, wachsende Session-Zahl. Kein Fehler im Log. |
|---|
Der Diagnose-Pfad: die Wartepyramide lesen
Die folgende Skizze zeigt eine typische Kaskade: Sieben Sessions stehen, aber nur eine ist die Ursache. SPID 87 und 91 sehen auf den ersten Blick wie Täter aus — sie blockieren ja andere —, sind aber selbst nur Glieder der Kette. Wer hier von unten aufräumt, killt die Falschen und gewinnt nichts.

Die Lesetechnik ist immer dieselbe: Der blocking_session_id nach oben folgen, bis sie 0 ist. Dort sitzt der Head-Blocker — in der Skizze SPID 62, und sein Steckbrief ist verräterisch: status = sleeping bei open_transaction_count = 1. Übersetzt: Die Session tut gerade gar nichts, hält aber eine offene Transaktion samt aller Sperren. Das ist das mit Abstand häufigste Bild — und der Grund, warum die Lösung selten „mehr Hardware“ heißt.
Erste Diagnose: konkrete Skripte
Das folgende Skript ist dein Hauptwerkzeug: Es zeigt alle wartenden Sessions mit Wartezeit, Wartetyp und Blocker — und identifiziert die Head-Blocker gleich mit, inklusive ihres letzten Statements:
|
— Blocking-Kette: Wer wartet auf wen, und wer ist der Kopf? SELECT r.session_id AS SPID, r.blocking_session_id AS BlkBy, r.wait_type, r.wait_time / 1000 AS WaitSec, s.login_name, s.host_name, s.program_name, t.text AS LaufendesStatement FROM sys.dm_exec_requests AS r JOIN sys.dm_exec_sessions AS s ON s.session_id = r.session_id OUTER APPLY sys.dm_exec_sql_text(r.sql_handle) AS t WHERE r.blocking_session_id <> 0 ORDER BY r.wait_time DESC;
— Head-Blocker-Kandidaten: blockieren andere, warten selbst nicht SELECT s.session_id, s.status, s.login_name, s.host_name, s.program_name, s.open_transaction_count, s.last_request_end_time FROM sys.dm_exec_sessions AS s WHERE s.session_id IN ( SELECT r.blocking_session_id FROM sys.dm_exec_requests AS r WHERE r.blocking_session_id <> 0 ) AND s.session_id NOT IN ( SELECT r.session_id FROM sys.dm_exec_requests AS r WHERE r.blocking_session_id <> 0 ); |
|---|
Das zweite Resultset ist die Antwort auf die Frage des Tages. Steht dort eine Session mit status = sleeping und open_transaction_count > 0, hast du den Klassiker: eine offene, vergessene Transaktion. last_request_end_time verrät dir sogar, seit wann sie schläft — und host_name plus login_name, wen du jetzt anrufen darfst. Was die schlafende Session zuletzt ausgeführt hat, holst du dir so:
|
— Letztes Statement einer schlafenden Session ansehen SELECT ib.event_info AS LetztesStatement FROM sys.dm_exec_input_buffer(62, NULL) AS ib; — SPID anpassen |
|---|
Für den Daueralltag lohnt der Blick auf sp_WhoIsActive von Adam Machanic — das Community-Standardwerkzeug, das Kette, Statements und Sperren in einer Ansicht aufbereitet. sp_who2 kannst du als historisches Erbstück weiter benutzen, um die BlkBy-Spalte zu scannen — aber für mehr als den ersten Schock-Überblick taugt es nicht: kein Statement-Text, keine Wartezeit, kein Kontext.
Die häufigsten Ursachen — und was du dagegen tust
1. Die vergessene offene Transaktion
Die Mutter aller Kaskaden: Jemand führt in SSMS BEGIN TRAN mit ein paar Updates aus, wird unterbrochen — Telefon, Meeting, Mittagspause — und das COMMIT bleibt liegen. Die Session schläft, die Sperren bleiben, und ab der nächsten kollidierenden Anfrage wächst die Pyramide. Dieselbe Mechanik produzieren Anwendungen, deren Fehlerbehandlung Transaktionen nicht sauber schließt: Die Exception fliegt, das Rollback fehlt, die Connection wandert mitsamt offener Transaktion zurück in den Pool. Sofortmaßnahme: Besitzer kontaktieren (committen oder rollbacken lassen) — oder KILL nach der Checkliste weiter unten. Strukturell: SET XACT_ABORT ON in Prozeduren und saubere TRY/CATCH-Blöcke mit Rollback im Fehlerfall.
2. Der Langläufer — Massenänderung ohne Batching
Das nächtliche „lösche alles älter als 90 Tage“ in einer einzigen Transaktion, das diesmal in den Geschäftstag hineinläuft: Solange die Riesen-Transaktion arbeitet, hält sie ihre Sperren — und ab etwa 5.000 Einzelsperren auf einem Objekt eskaliert SQL Server auf eine Tabellensperre, die dann wirklich jeden aussperrt. Lösung: Massenänderungen in Batches von ein paar tausend Zeilen zerlegen, nach jedem Batch committen. Die Sperren kommen früh frei, die Eskalation bleibt aus, und ein Abbruch kostet nur den letzten Batch statt vier Stunden Rollback. Wie die Eskalations-Mechanik im Detail funktioniert, steht im Deadlock-Beitrag — beide Symptome haben hier dieselbe Wurzel.
3. Fehlende Indizes — Updates sperren mehr, als sie ändern
Ein UPDATE … WHERE Kundennummer = 4711 ohne Index auf der Kundennummer muss die Tabelle scannen — und sperrt auf dem Weg weit mehr Zeilen, als es ändert. Je breiter der Scan, desto mehr unbeteiligte Sessions geraten in die Kette. Verdächtig ist Blocking zwischen Prozessen, die fachlich nichts miteinander zu tun haben: Wenn die Rechnungsbuchung den Adress-Import blockiert, sperrt da jemand großflächiger als nötig. Ein gezielter Index auf die Filterspalten verengt die Sperren auf das tatsächlich Betroffene — und beschleunigt die Query gleich mit. Treibt derselbe Scan auch noch die CPU, lies parallel den Beitrag zur CPU bei 100 Prozent.
4. Leser blockieren Schreiber — der Fall für RCSI
Der Monatsbericht liest zwanzig Minuten lang quer durch die Auftragstabelle und hält dabei Lesesperren — die Sachbearbeitung will buchen und wartet. Unter dem klassischen Read Committed blockieren sich Leser und Schreiber gegenseitig, und auf gemischten Systemen (OLTP plus Reporting auf derselben Datenbank) ist das eine Dauerquelle für Kaskaden. Read Committed Snapshot Isolation (RCSI) löst genau diese Klasse: Leser arbeiten gegen Zeilenversionen aus der tempdb und halten keine Sperren mehr, die Schreiber aufhalten. Voraussetzung ist eine tempdb, die den Version Store verkraftet. Writer-Writer-Blocking — zwei UPDATEs auf dieselben Zeilen — bleibt allerdings bestehen: Das ist Physik, kein Konfigurationsproblem.
|
— RCSI pruefen und aktivieren SELECT name, is_read_committed_snapshot_on FROM sys.databases WHERE name = N'MeineDB';
ALTER DATABASE [MeineDB] SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE; |
|---|
Die KILL-Entscheidung — gezielt statt panisch
Irgendwann steht die Frage im Raum: killen oder warten? KILL 62 beendet die Head-Blocker-Session — aber es bricht nicht einfach ab, sondern rollt alle Änderungen der offenen Transaktion zurück. Und dieser Rollback kann länger dauern als die bisherige Laufzeit der Transaktion, denn er läuft single-threaded. Wer das Vier-Stunden-Delete bei Stunde dreieinhalb killt, hat danach nicht Ruhe, sondern weitere Stunden Rollback — bei weiterhin gehaltenen Sperren. Deshalb vor jedem KILL drei Fragen: Ist der Head-Blocker eindeutig identifiziert? Löst er sich absehbar nicht selbst (schlafende Session, Besitzer unerreichbar)? Und ist der Rollback-Preis kleiner als der Schaden des Weiterwartens? Bei der schlafenden SSMS-Session mit drei geänderten Zeilen: klares Ja. Beim Massen-Delete: erst nachdenken, dann entscheiden.
|
— Head-Blocker beenden (Rollback beachten!) KILL 62;
— Rollback-Fortschritt einer gekillten Session verfolgen KILL 62 WITH STATUSONLY; — Ausgabe: SPID 62: … Estimated rollback completion: 47%. … |
|---|
Was du auf keinen Fall tun solltest
|
Die drei häufigsten Anfängerfehler 1. sp_who2 öffnen, „27 blockierte Sessions!“ rufen und in Panik verfallen — statt der blocking_session_id bis zur 0 zu folgen. Eine Kaskade hat einen Kopf, nicht siebenundzwanzig. |
|---|
Nachsorge: Damit es nicht wiederkommt
Drei strukturelle Maßnahmen nach dem akuten Stau: Erstens, ein Frühwarnsystem einrichten — SQL Server kann ab einer konfigurierbaren Blockierdauer einen Blocked Process Report erzeugen, den du per Extended Events einsammelst. Damit siehst du Blocking-Muster, bevor der Fachbereich anruft:
|
— Schwellenwert: Report ab 10 Sekunden Blockierdauer EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'blocked process threshold (s)', 10; RECONFIGURE; GO — XE-Session zum Einsammeln der Reports CREATE EVENT SESSION [BlockedProcesses] ON SERVER ADD EVENT sqlserver.blocked_process_report ADD TARGET package0.event_file ( SET filename = N'BlockedProcesses', max_file_size = 50, max_rollover_files = 4 ) WITH (STARTUP_STATE = ON); GO ALTER EVENT SESSION [BlockedProcesses] ON SERVER STATE = START; |
|---|
Zweitens, die Transaktions-Hygiene in den Code-Review aufnehmen: kurze Transaktionen, XACT_ABORT, sauberes Rollback im Fehlerfall, Batching bei Massenänderungen — die billigste Prävention steht im Entwickler-Wiki, nicht in der Serverkonfiguration. Drittens, die LCK-Wartetypen in die Baseline aufnehmen: Wenn der LCK_M_*-Anteil an den Gesamt-Waits schleichend steigt, kündigt sich die nächste Kaskade an. Wie du so eine Baseline aufsetzt, zeigt der Wait-Stats-Beitrag der GRÜN-Stufe.
Vertiefung in der Buchreihe
Sperr-Architektur und transaktionssicherer Anwendungscode füllen in der Buchreihe „SQL Server in der Praxis“ eigene Kapitel — alle Bände in der Übersicht auf der SQL-Pillar-Seite unter /sql-server/.
|
Empfohlene Bände Band 1: Performance & Troubleshooting — Lock-Modi und ihre Kompatibilitätsmatrix, Lock Escalation, Isolation Level, Version Store und die LCK-Wartetypen im Diagnose-Gesamtbild. |
|---|
Verwandte Notfälle im Notarztwagen
Blocking hat Nachbarn — diese Beiträge aus dem Cluster lohnen den Blick:
Drei Wege, wenn die Kaskade jetzt gerade steht
Festpreis-Analyse
Die Kaskaden kommen wieder und wieder, und intern fehlt die Zeit, den Mustern auf den Grund zu gehen? Dafür gibt es die Festpreis-Analyse: Ich werte Blocked Process Reports und Wait Stats aus, identifiziere die wiederkehrenden Head-Blocker, prüfe Transaktions-Muster und Indizes und liefere einen Bericht mit priorisierten Maßnahmen — zum vereinbarten Festpreis, ohne Stundenzähler.
Akut-Hotline
Wenn die Produktion gerade jetzt im Stau steht: anrufen. Halte einen Zugang mit sysadmin- oder VIEW-SERVER-STATE-Rechten bereit und die Information, was heute anders ist als gestern — Release, Sonderlauf, Stichtagsverarbeitung. Mit dem Ketten-Skript aus diesem Beitrag ist der Head-Blocker in Minuten identifiziert, die Entscheidung über den Eingriff treffen wir gemeinsam.
Selbst-Diagnose-Kit
Du willst gerüstet sein, bevor es das nächste Mal staut? Das Diagnose-Kit bündelt die Skripte aus diesem Beitrag — Ketten-Analyse, Head-Blocker-Identifikation, Input-Buffer-Check, Blocked-Process-Setup — als ausführbares Paket mit Auswertungshilfe und der KILL-Checkliste zum Ausdrucken.
|
Ein letzter Tipp Konfiguriere den Blocked Process Threshold aus dem Nachsorge-Abschnitt noch diese Woche — zehn Sekunden, XE-Session dazu, fertig. Das kostet dich fünf Minuten und keinerlei Performance. Beim nächsten Stau hast du dann nicht nur das Live-Bild, sondern die komplette Vorgeschichte: wer, wann, wie lange, mit welchem Statement. Der Unterschied zwischen Raten und Wissen ist genau eine sp_configure-Zeile. |
|---|
Häufige Fragen (FAQ)
Diese Sektion ist auf häufige Suchanfragen abgestimmt. Sie hilft dir, schnell den Punkt zu finden, der dir gerade akut weiterhilft — und sie ist gleichzeitig die Eingabe für das FAQPage-Schema auf der Seite.
Was ist Blocking im SQL Server?
Blocking entsteht, wenn eine Session eine Sperre anfordert, die mit einer bereits vergebenen Sperre unvereinbar ist — dann wartet sie, bis der Inhaber freigibt. Das ist zunächst völlig normal und passiert auf jedem Server tausendfach pro Stunde, meist im Millisekundenbereich. Zum Problem wird Blocking erst, wenn der Inhaber nicht freigibt: Eine lange oder vergessene Transaktion hält ihre Sperren fest, dahinter staut sich eine wachsende Kette wartender Sessions — die Blocking-Kaskade. Die Anwendung wird zäh oder läuft in Timeouts. Wie du die Kette auflöst, zeigt dieser Beitrag Schritt für Schritt.
Wie finde ich den Head-Blocker?
Folge der blocking_session_id nach oben: Jede wartende Session zeigt in sys.dm_exec_requests, auf wen sie wartet. Der Head-Blocker ist die Session am oberen Ende der Kette — sie blockiert andere, wartet aber selbst auf niemanden (blocking_session_id = 0 bzw. sie taucht gar nicht als wartender Request auf, weil sie schläft). Das Diagnose-Skript in diesem Beitrag erledigt die Kletterei automatisch und liefert Head-Blocker samt letztem Statement, Login und Status in einer Abfrage. Wichtig: Nie die mittleren Glieder der Kette anfassen — die sind selbst Opfer.
Was ist der Unterschied zwischen Blocking und Deadlock?
Blocking ist lineares Warten: A wartet auf B, und sobald B committet, geht es weiter — unangenehm, aber endlich. Ein Deadlock ist zirkuläres Warten: A wartet auf B und B gleichzeitig auf A — das löst sich nie von selbst, weshalb der Deadlock-Monitor eine der Sessions opfert und ihr Error 1205 schickt. Praktische Faustregel: Blocking erzeugt Langsamkeit und Timeouts, Deadlocks erzeugen Fehlermeldungen. Wenn deine Anwendung 1205er wirft, bist du im Deadlock-Beitrag des Notarztwagens richtig (/sql-server-deadlocks/) — wenn alles nur zäh wird und steht, hier.
Wie nutze ich sp_who2?
EXEC sp_who2 liefert alle Sessions mit der berühmten Spalte BlkBy — steht dort eine SPID, wartet die Zeile auf genau diese Session. Für den schnellen Überblick taugt das: BlkBy-Spalte scannen, Ketten erkennen, Head-Blocker erahnen. Für echte Diagnose ist sp_who2 aber zu dünn: kein Statement-Text, keine Wartezeit, keine Sperr-Details, und bei langen Ketten wird das manuelle Verfolgen mühsam. Nimm stattdessen die DMV-Abfrage aus diesem Beitrag oder das Community-Werkzeug sp_WhoIsActive — beide zeigen dir die Kette samt Kontext in einem Rutsch.
Was bedeutet LCK_M_X als Wartetyp?
LCK_M_X heißt: Die Session wartet darauf, eine exklusive Sperre (X = exclusive) zu bekommen — typischerweise will sie Daten ändern, die ein anderer gerade gesperrt hält. Die LCK_M_*-Familie liest sich nach demselben Muster: LCK_M_S wartet auf eine Lesesperre (shared), LCK_M_U auf eine Update-Sperre, LCK_M_IX auf eine Intent-Exclusive-Sperre auf übergeordneter Ebene. Dominieren LCK_M_*-Wartetypen deine Wait Stats, hast du ein Sperr- bzw. Blocking-Thema — wie du Wartetypen im Gesamtbild interpretierst, zeigt der Wait-Stats-Beitrag der GRÜN-Stufe.
Wann sollte ich KILL verwenden?
KILL ist gerechtfertigt, wenn drei Bedingungen zusammenkommen: Der Head-Blocker ist eindeutig identifiziert, er wird sich absehbar nicht von selbst auflösen (schlafende Session mit offener Transaktion, Kollege im Feierabend), und der Schaden des Wartens übersteigt den Schaden des Rollbacks. Letzteres ist der Knackpunkt: KILL bricht nicht einfach ab, sondern rollt alle Änderungen der Transaktion zurück — und das kann länger dauern als die bisherige Laufzeit. Vorher abschätzen, mit KILL … WITH STATUSONLY den Fortschritt verfolgen. Details und die Checkliste stehen im KILL-Abschnitt dieses Beitrags.
Wie verhindere ich Blocking-Kaskaden?
Vier Hebel mit der größten Wirkung: Erstens kurze Transaktionen — Daten vorher sammeln, dann öffnen, schreiben, committen; niemals in offener Transaktion auf externe Systeme oder Benutzer warten. Zweitens Batching bei Massenänderungen, damit Sperren früh freikommen und keine Lock Escalation greift. Drittens passende Indizes, damit Updates nur die Zeilen sperren, die sie wirklich ändern. Viertens Read Committed Snapshot Isolation, damit Berichte und Leser die Schreiber gar nicht erst aufhalten. Dazu ein Frühwarnsystem: der Blocked Process Report per Extended Events — das Skript steht im Nachsorge-Abschnitt.
