- 1 Section
- 10 Lessons
- unbegrenzt
- Datenintegrität & Transaktionen10
- 1.1Integritätstypen
- 1.2Primary Key, Foreign Key, Constraints
- 1.3Referenzielle Integrität: ON DELETE, ON UPDATE
- 1.4Was sind Transaktionen?
- 1.5ACID-Eigenschaften
- 1.6Transaktions-Isolation und Anomalien
- 1.7COMMIT, ROLLBACK und SAVEPOINT
- 1.8Deadlocks: Entstehung und Vermeidung
- 1.9Datenbankindizes und Performance
- 1.10Aufgaben Datenintegrität
Deadlocks: Entstehung und Vermeidung
In Lektion 6 haben wir gesehen, wie das DBMS Isolation umsetzt – unter anderem über Sperren (Locks). Wenn eine Transaktion eine Zeile ändert, hält sie auf dieser Zeile eine exklusive Sperre bis zum COMMIT oder ROLLBACK. Andere Transaktionen, die diese Zeile auch ändern wollen, warten geduldig.
Was passiert aber, wenn Transaktion A auf Zeile 1 wartet, weil Transaktion B sie hält – und Transaktion B gleichzeitig auf Zeile 2 wartet, weil Transaktion A sie hält? Antwort: beide warten ewig aufeinander. Keine kann weiter. Das ist ein Deadlock. Diese Lektion zeigt wie Deadlocks entstehen, wie das DBMS sie erkennt und auflöst, und – am wichtigsten – wie du sie in deinem Code vermeidest.
1) Das Restaurant-Bild
Stell dir zwei Gäste im Restaurant vor: jeder bestellt Suppe, die mit Löffel und Gabel gegessen werden soll. Auf dem Tisch liegt ein Löffel und eine Gabel. Gast A schnappt sich den Löffel, Gast B die Gabel. Jetzt wartet A auf B's Gabel, B wartet auf A's Löffel. Keiner gibt nach, keiner isst – sie sitzen ewig da. Das ist die exakte Situation in einer Datenbank, nur mit Zeilen statt Besteck.
Der bekannte „Dining Philosophers"-Klassiker beschreibt genau das. In der Praxis braucht es zwei Voraussetzungen: mehrere Ressourcen, die in unterschiedlicher Reihenfolge gesperrt werden. Beides ist im SQL-Alltag schnell der Fall.
2) Ein klassisches Deadlock-Szenario
Banküberweisung in beide Richtungen gleichzeitig – das einfachste Beispiel. Zwei Sessions wollen je 100 € zwischen Konto A und Konto B verschieben, aber in entgegengesetzter Richtung:
3) Wie das DBMS Deadlocks erkennt
Das DBMS führt im Hintergrund einen Wait-for-Graph: jeder Knoten ist eine Transaktion, jede Kante zeigt „wartet auf". Wenn eine Transaktion eine Sperre anfordert, die schon vergeben ist, wird die entsprechende Kante hinzugefügt. Wenn dabei ein Zyklus entsteht, ist es ein Deadlock:
Der Zyklus T1 → T2 → T1 ist die Signatur eines Deadlocks.
4) Wie sich ein Deadlock-Fehler im Code zeigt
Wenn deine Anwendung das Opfer ist, bekommst du eine sehr spezifische Fehlermeldung. In MySQL sieht das so aus:
try restarting transaction
Wichtig ist der SQL-State 40001 – das ist der Standard-Code für „Serialization Failure / Deadlock detected". In PostgreSQL ist es der gleiche Code, der Text leicht anders. Andere Codes für ähnliche Konflikte: 40P01 (PostgreSQL „deadlock detected"), ORA-00060 (Oracle „deadlock detected"). Allen gemein: deine Transaktion wurde bereits zurückgerollt. Du musst kein ROLLBACK mehr senden – die Aufgabe ist erledigt.
Was du tun musst: die ganze Transaktion neu starten. Genau dafür gibt es das Retry-Pattern:
for (let attempt = 1; attempt <= 3; attempt++) {
try {
await db.query('BEGIN');
// ... Transaktion ...
await db.query('COMMIT');
break; // erfolgreich
} catch (err) {
if (err.code === '40001' && attempt < 3) {
await sleep(Math.random() * 100); // random Backoff
continue;
}
throw err;
}
}
Das Random-Backoff verhindert, dass beide gekoppelten Sessions nach einer fest definierten Zeit gleichzeitig neu starten – und sich wieder gegenseitig blockieren.
5) Strategien zur Vermeidung
Retry ist Symptombehandlung. Besser ist, Deadlocks gar nicht erst entstehen zu lassen. Es gibt vier bewährte Strategien – klick dich durch:
6) Deadlock vs. einfaches Lock-Wait
Wichtig zu unterscheiden: nicht jedes Warten ist ein Deadlock. Wenn Transaktion A einfach nur auf B's Sperre wartet, ohne dass B umgekehrt auf etwas wartet, ist das ein normales Lock-Wait – das löst sich auf, sobald B fertig ist. Erst wenn ein Zyklus entsteht (A wartet auf B wartet auf A), ist es ein Deadlock.
Beide Phänomene haben getrennte Timeouts:
- Lock-Wait-Timeout (MySQL:
innodb_lock_wait_timeout, Default 50s): wie lange eine Transaktion auf eine Sperre wartet bevor sie aufgibt. Schlägt mitERROR 1205fehl. - Deadlock-Detection (MySQL:
innodb_deadlock_detect = ON, Default an): das DBMS prüft aktiv den Wait-for-Graph und greift sofort ein.
Praxis: bei langen Hänger-Symptomen in Production hilft SHOW ENGINE INNODB STATUS; in MySQL – das zeigt aktuelle Sperren und die letzten Deadlocks mit allen Details. PostgreSQL bietet SELECT * FROM pg_locks; und Logging-Optionen für lange Locks.
7) Monitoring & Diagnose
In großen Systemen kommen Deadlocks unter Last vor – nicht laufend, aber regelmäßig. Wichtig ist sie zu messen, nicht zu ignorieren. Drei Diagnose-Tools:
- InnoDB-Statusausgabe:
SHOW ENGINE INNODB STATUS\Gzeigt unter „LATEST DETECTED DEADLOCK" das letzte Ereignis mit beteiligten Queries und gehaltenen Sperren. Goldstandard für Post-mortem-Analyse. - Performance Schema (MySQL): Tabelle
events_statements_historykombiniert mitdata_lockszeigt was zur Deadlock-Zeit lief. - Anwendungs-Logging: jeder gefangene Deadlock-Error sollte mit Stack-Trace und beteiligten Tabellen geloggt werden. Häufungen weisen auf Hot-Spots hin, die du anpassen kannst.
Eine niedrige Deadlock-Rate (z.B. <0,01% aller Transaktionen) ist normal. Wenn 5% deiner Transaktionen Deadlocks sehen, hast du ein Strukturproblem – meist falsche Sperrreihenfolge oder zu lange Transaktionen.
8) Wann Deadlocks akzeptabel sind
Vollständig deadlock-freier Code wäre teuer – jede Transaktion müsste pessimistisch das gesamte System sperren bevor sie startet, oder über Anwendungslogik-Locks gehen, die noch komplexer sind. In der Praxis akzeptiert man eine kleine Deadlock-Quote als Kosten der Nebenläufigkeit.
Die richtige Frage ist nicht „wie verhindere ich JEDEN Deadlock?", sondern „wie minimiere ich die Häufigkeit und wie handle ich sie sauber wenn sie passieren?". Antwort: konsistente Sperrreihenfolge, kurze Transaktionen, Retry-Pattern auf Anwendungsseite. Damit lebt jedes professionelle System.
Zusammenfassung
Ein Deadlock ist eine zyklische Wartesituation zwischen zwei oder mehr Transaktionen: jede hält eine Sperre, auf die eine andere wartet. Das DBMS erkennt solche Zyklen über einen Wait-for-Graph, wählt eines der Opfer aus, rollt es zurück (Sperren werden freigegeben) und meldet dem Klienten einen Deadlock-Fehler (SQL-State 40001, MySQL ERROR 1213). Die andere Transaktion läuft weiter. Vermeidungsstrategien: konsistente Sperrreihenfolge (wichtigste!), kurze Transaktionen, geringere Isolationsstufe wo akzeptabel, gezieltes SELECT … FOR UPDATE zur Sperrlokalisierung. Unterscheide Deadlock (zyklisch, sofort erkannt) von Lock-Wait (einfaches Warten, läuft nach Timeout ab). Im Anwendungscode immer ein Retry-Pattern mit Random Backoff für Deadlock-Errors implementieren. Eine niedrige Quote ist normal und akzeptabel – Häufungen weisen auf strukturelle Probleme hin.
