Deadlock-Vermeidung: Strategien für den Anwendungscode
AI generated
SELECT
JOIN
SQL · Transaktionen · Nebenläufigkeit
Deadlock-Vermeidung: Strategien für den Anwendungscode
von Lock Ordering bis Retry-with-Backoff

Ein Deadlock entsteht, wenn sich zwei Transaktionen gegenseitig blockieren, weil jede auf eine Ressource wartet, die die andere hält. Konsistente Sperrreihenfolge im Anwendungscode, kurze Transaktionen und robuste Retry-Logik verhindern die meisten Deadlocks, bevor die Datenbank überhaupt eingreifen muss.

15 Min. Lesezeit Lock Ordering · Retry-Pattern · Isolation Levels PostgreSQL · MySQL/InnoDB · SQL Server

1. Was ein Deadlock ist und warum er entsteht

Ein Deadlock entsteht, wenn zwei oder mehr Transaktionen sich gegenseitig blockieren, weil jede auf eine Sperre wartet, die eine andere Transaktion hält, und keine der beteiligten Transaktionen jemals freiwillig aufgibt. Das klassische Beispiel: Transaktion A sperrt Zeile 1 und will danach Zeile 2 sperren, während Transaktion B in umgekehrter Reihenfolge arbeitet, Zeile 2 bereits gesperrt hat und nun auf Zeile 1 wartet. Beide Transaktionen warten unendlich, bis eine externe Instanz eingreift. Ohne diese Instanz würde das System einfach hängen bleiben.

Deadlocks sind kein Zeichen eines fehlerhaften Datenbanksystems, sondern eine unvermeidliche Konsequenz von Nebenläufigkeit und Sperrmechanismen, sobald mehrere Transaktionen gleichzeitig auf dieselben Ressourcen in unterschiedlicher Reihenfolge zugreifen. Das Ziel ist daher nicht, Deadlocks technisch unmöglich zu machen, sondern ihre Häufigkeit durch gezielte Muster im Anwendungscode drastisch zu reduzieren.

Ein Deadlock ist damit grundsätzlich verschieden von einfacher Lock Contention. Bei normaler Lock Contention wartet eine Transaktion, bis eine Sperre frei wird, und kann danach fortfahren, sobald die haltende Transaktion committet oder rollt zurück. Bei einem echten Deadlock gibt es keinen Ausweg ohne aktives Eingreifen, weil die Wartebeziehung einen Zyklus bildet. Relationale Datenbanken erkennen solche Zyklen aktiv und lösen sie auf, indem sie eine der beteiligten Transaktionen als Opfer auswählen und mit einem Fehler zurückrollen.

Die eigentliche Herausforderung liegt nicht in der Erkennung, das übernimmt die Datenbank zuverlässig, sondern in der Deadlock-Vermeidung im Anwendungscode. Wer die typischen Muster kennt, die zu Deadlocks führen, kann sie durch bewusste Reihenfolge, kurze Transaktionsdauer und geeignete Fehlerbehandlung in den meisten Fällen von vornherein verhindern, statt sich auf die reaktive Fehlerbehandlung der Datenbank zu verlassen.

Die folgenden Abschnitte behandeln jede dieser Strategien im Detail und zeigen anhand konkreter SQL-Beispiele, wie sich Deadlock-Vermeidung systematisch in bestehenden Anwendungscode integrieren lässt, ohne die Architektur grundlegend umzubauen.

2. Consistent Lock Ordering als Prävention

Die wirksamste Strategie gegen Deadlocks heißt Consistent Lock Ordering: Alle Transaktionen, die dieselben Ressourcen benötigen, sperren sie stets in derselben Reihenfolge, zum Beispiel immer aufsteigend nach Primärschlüssel. Wenn jede Transaktion, die Zeilen aus den Tabellen accounts und transfers benötigt, immer zuerst accounts und danach transfers sperrt, und innerhalb von accounts immer nach aufsteigender ID vorgeht, kann kein zyklischer Wartegraph mehr entstehen. Zwei Transaktionen, die auf dieselben Ressourcen zugreifen, konkurrieren dann höchstens sequenziell um eine Sperre, aber niemals im Kreis.

In der Praxis bedeutet das für Anwendungscode, der zum Beispiel Geldbeträge zwischen zwei Konten überweist: Statt die Konten in der Reihenfolge zu sperren, in der sie im Request ankommen, sortiert man die beteiligten IDs vor der Transaktion und sperrt in dieser festen Reihenfolge. Dieses Muster zur Deadlock-Vermeidung kostet fast nichts an Performance, verhindert aber genau die Deadlock-Klasse, die bei Überweisungslogik am häufigsten auftritt, wenn zwei parallele Transfers in entgegengesetzter Richtung laufen.


-- WRONG: lock order depends on argument order, causes deadlocks
-- transfer(from_id, to_id, amount) called concurrently in both directions
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = :from_id;
UPDATE accounts SET balance = balance + 100 WHERE id = :to_id;
COMMIT;

-- RIGHT: always lock rows in ascending id order, regardless of transfer direction
BEGIN;
SELECT id FROM accounts WHERE id IN (:from_id, :to_id) ORDER BY id FOR UPDATE;
UPDATE accounts SET balance = balance - 100 WHERE id = :from_id;
UPDATE accounts SET balance = balance + 100 WHERE id = :to_id;
COMMIT;
-- Both concurrent transfers now acquire locks in the same sequence,
-- so no circular wait can form between them

3. Transaktionen kurz halten: Dauer und Scope

Je länger eine Transaktion offen bleibt, desto größer ist das Zeitfenster, in dem sie Sperren hält, und desto höher die Wahrscheinlichkeit, dass eine andere Transaktion mit ihr kollidiert. Ein zentraler Baustein der Deadlock-Vermeidung ist daher, jede Transaktion so kurz wie möglich zu halten. Netzwerk-Roundtrips, externe API-Aufrufe, Dateizugriffe oder Berechnungen, die nicht direkt von der Transaktion abhängen, gehören außerhalb der BEGIN/COMMIT-Klammer, nicht dazwischen.

Ein häufiges Anti-Pattern in Anwendungscode: Eine Transaktion wird geöffnet, dann folgt ein HTTP-Aufruf an einen externen Zahlungsdienstleister, und erst nach dessen Antwort werden die Datenbankänderungen committet. Während der Wartezeit auf die externe Antwort hält die Transaktion ihre Sperren, blockiert andere Transaktionen und erhöht das Deadlock-Risiko drastisch, weil die Sperrdauer nicht mehr im Millisekundenbereich liegt, sondern im Sekundenbereich oder länger. Die korrekte Reihenfolge: erst die externe Kommunikation abschließen, dann die Transaktion mit allen notwendigen Schreiboperationen kurz und zügig ausführen.

Auch innerhalb der Transaktion selbst lohnt es sich, alle lesenden Zugriffe, die keine Sperre benötigen, vor die schreibenden Operationen zu ziehen und die Anzahl der Statements zu minimieren. Batch-Updates statt vieler einzelner UPDATE-Statements in einer Schleife reduzieren nicht nur die Netzwerklast, sondern auch die Zeit, in der Sperren gehalten werden, und damit direkt das Risiko eines Deadlocks.

4. Retry-with-Backoff im Anwendungscode

Selbst mit konsistenter Lock-Reihenfolge und kurzen Transaktionen lassen sich Deadlocks in komplexen Systemen nicht vollständig ausschließen, insbesondere wenn mehrere unabhängige Codepfade dieselben Ressourcen aus unterschiedlichen Kontexten heraus sperren. Deshalb gehört zu jeder soliden Deadlock-Vermeidung-Strategie auch ein Retry-Mechanismus im Anwendungscode, der die von der Datenbank als Opfer ausgewählte Transaktion automatisch erneut ausführt.

Die Datenbank signalisiert einen Deadlock über einen spezifischen Fehlercode, in PostgreSQL den SQLSTATE 40P01, in MySQL/InnoDB den Fehlercode 1213. Anwendungscode sollte diesen Fehler explizit abfangen und von anderen SQL-Fehlern unterscheiden, denn ein Deadlock ist ein transienter Zustand, der bei erneuter Ausführung mit hoher Wahrscheinlichkeit nicht mehr auftritt. Ein pauschales Retry für alle Fehlerarten wäre dagegen gefährlich, weil es bei echten Constraint-Verletzungen oder Logikfehlern sinnlos wiederholt und nur Last erzeugt.

Exponentielles Backoff mit Jitter ist dabei wichtiger, als es zunächst scheint: Ohne zufällige Verzögerung würden zwei Transaktionen, die gerade in einen Deadlock gelaufen sind, oft synchron wieder aufeinandertreffen und erneut kollidieren. Ein zufälliger Jitter-Anteil in der Wartezeit entkoppelt die Retry-Versuche zeitlich voneinander und senkt die Wahrscheinlichkeit eines erneuten Deadlocks bei jedem weiteren Versuch deutlich.


-- Pseudocode pattern for retry-with-backoff around a transaction
-- Language-agnostic: applies to any client library

max_attempts = 5
attempt = 0

while attempt < max_attempts:
    try:
        begin_transaction()
        # SELECT ... FOR UPDATE in fixed, ascending id order
        execute("SELECT id FROM accounts WHERE id IN (?, ?) ORDER BY id FOR UPDATE", from_id, to_id)
        execute("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, from_id)
        execute("UPDATE accounts SET balance = balance + ? WHERE id = ?", amount, to_id)
        commit_transaction()
        break
    except DeadlockError as e:          # SQLSTATE 40P01 / MySQL error 1213
        rollback_transaction()
        attempt += 1
        if attempt >= max_attempts:
            raise
        backoff_ms = (2 ** attempt) * 50 + random_jitter(0, 50)
        sleep(backoff_ms)

5. Isolation Levels und ihr Einfluss auf Deadlocks

Das gewählte Isolation Level beeinflusst direkt, wie viele Sperren eine Transaktion hält und wie lange, und damit auch, wie hoch das Deadlock-Risiko ausfällt. Unter READ COMMITTED, dem Standard in PostgreSQL und Oracle, werden Lesesperren in der Regel sofort nach dem jeweiligen Statement freigegeben, während Schreibsperren bis zum Transaktionsende gehalten werden. Unter REPEATABLE READ, dem Standard in MySQL/InnoDB, hält die Transaktion zusätzlich Range-Sperren, die auch Phantom-Reads verhindern sollen, was das Deadlock-Risiko bei komplexen Range-Queries spürbar erhöht.

Ein höheres Isolation Level wie SERIALIZABLE reduziert Anomalien, erhöht aber tendenziell die Anzahl der gehaltenen Sperren oder, bei optimistischen Implementierungen wie in PostgreSQL, die Anzahl der Serialization-Failure-Fehler, die ebenfalls einen Retry im Anwendungscode erfordern. Die Wahl des Isolation Levels ist damit kein rein akademisches Detail, sondern hat direkten Einfluss auf die Häufigkeit von Deadlocks und Serialization-Fehlern in Produktionssystemen unter hoher Nebenläufigkeit.


-- Setting the isolation level explicitly per transaction, not just per session
-- REPEATABLE READ in MySQL/InnoDB acquires additional gap/range locks
BEGIN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
UPDATE accounts SET balance = balance - 50 WHERE id = 7;
COMMIT;
-- Fewer locks held, lower deadlock probability, but weaker repeatability guarantees

-- Same operation under a stricter level increases lock scope
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
UPDATE accounts SET balance = balance - 50 WHERE id = 7;
COMMIT;
-- More locks held for the transaction duration, higher deadlock risk under concurrency

6. Deadlock-Erkennung: Wie die Datenbank reagiert

Relationale Datenbanken erkennen Deadlocks über einen internen Wait-for-Graph: Jede Transaktion, die auf eine Sperre wartet, wird als Kante zu der Transaktion eingetragen, die diese Sperre hält. Findet der Deadlock-Detector einen Zyklus in diesem Graphen, wählt er eine Transaktion als Opfer aus, meist die mit dem geringsten geleisteten Aufwand oder der jüngeren Startzeit, und bricht sie mit einem Fehler ab, während die andere Transaktion normal fortfahren kann. Diese Prüfung läuft periodisch im Hintergrund, in PostgreSQL etwa alle Sekunde als konfigurierbarer deadlock_timeout.

Wichtig für die Deadlock-Vermeidung im Anwendungscode: Die abgebrochene Transaktion wird vollständig zurückgerollt, alle ihre Änderungen sind verloren, und der Client erhält einen spezifischen Fehler. Der Anwendungscode muss diesen Zustand als normalen, erwarteten Betriebsfall behandeln, nicht als Ausnahmefehler, der das gesamte Request fehlschlagen lässt. Ein sauber implementiertes Retry-Pattern verwandelt einen Deadlock für den Endnutzer in eine unmerkliche, minimal verzögerte Ausführung statt in einen sichtbaren Fehler.

7. Row-Level Locking vs. Table-Level Locking

Die Granularität der Sperren beeinflusst maßgeblich, wie oft Deadlocks überhaupt auftreten können. Row-Level Locking, wie es InnoDB und PostgreSQL standardmäßig für UPDATE und SELECT ... FOR UPDATE verwenden, sperrt nur die tatsächlich betroffenen Zeilen und lässt parallele Transaktionen auf anderen Zeilen derselben Tabelle ungehindert weiterarbeiten. Table-Level Locking dagegen, etwa durch explizites LOCK TABLE oder durch Statements, die implizit die gesamte Tabelle sperren, erhöht die Kollisionswahrscheinlichkeit drastisch, weil auch völlig unabhängige Zeilen plötzlich um dieselbe Ressource konkurrieren.

Ein häufig übersehener Fall: Fehlende Indizes auf Fremdschlüsselspalten können dazu führen, dass die Datenbank bei UPDATE- oder DELETE-Statements mit Referenzprüfung mehr Zeilen sperrt als nötig, weil sie ohne Index einen vollständigen Tabellenscan mit Sperren durchführen muss, statt gezielt über den Index die relevante Zeile zu treffen. Für die Deadlock-Vermeidung ist ein sauberer Index auf allen Spalten, die in WHERE-Klauseln von UPDATE/DELETE-Statements innerhalb von Transaktionen verwendet werden, deshalb keine reine Performance-Optimierung, sondern direkt relevant für die Anzahl der gehaltenen Sperren.


-- WRONG: no index on the foreign key column, forces a wider lock scan
CREATE TABLE order_items (
  id INT PRIMARY KEY,
  order_id INT NOT NULL REFERENCES orders(id),
  product_id INT NOT NULL
);
-- DELETE FROM orders WHERE id = 42 now scans order_items without an index,
-- locking far more rows than the ones actually referencing order_id = 42

-- RIGHT: index the foreign key column explicitly
CREATE INDEX idx_order_items_order_id ON order_items (order_id);
-- The same DELETE now locks only the rows matching order_id = 42

8. Monitoring und Logging von Deadlocks

Ohne systematisches Monitoring bleiben Deadlocks oft unsichtbar, bis Nutzer sich über sporadisch fehlschlagende Requests beschweren. Alle gängigen Datenbanken bieten Bordmittel, um Deadlocks zu protokollieren: PostgreSQL schreibt Deadlock-Details bei aktiviertem log_lock_waits ins Server-Log, MySQL/InnoDB stellt die letzten Deadlocks über SHOW ENGINE INNODB STATUS bereit, SQL Server bietet Extended Events und den Deadlock-Graph in XML-Form.

Für die langfristige Deadlock-Vermeidung lohnt es sich, diese Logs regelmäßig auszuwerten und Muster zu identifizieren: Treten Deadlocks immer zwischen denselben zwei Tabellen auf? Immer zur gleichen Tageszeit bei Batch-Jobs? Das sind starke Hinweise auf fehlende Lock-Ordering-Disziplin oder auf Transaktionen, die zu lange offen bleiben. Ein Dashboard, das die Deadlock-Rate pro Stunde neben der Request-Rate zeigt, macht Regressionen nach Deployments sofort sichtbar, statt sie erst durch Support-Tickets zu bemerken.

Ein oft übersehener Baustein im Monitoring ist die Korrelation von Deadlock-Ereignissen mit Deployment-Zeitpunkten. Wird ein neues Feature ausgerollt, das eine zusätzliche Tabelle in eine bestehende Transaktion einbezieht, kann sich die Lock-Reihenfolge unbemerkt ändern und eine bisher unauffällige Codepfad-Kombination plötzlich in Konflikt mit einem anderen bringen. Ein automatisiertes Alerting, das bei einem sprunghaften Anstieg der Deadlock-Rate innerhalb der ersten Stunden nach einem Deployment eine Warnung auslöst, verkürzt die Zeit bis zur Identifikation des verursachenden Commits erheblich und verhindert, dass sich ein fehlerhaftes Lock-Ordering-Muster über Wochen unbemerkt in der Codebasis verbreitet.


-- PostgreSQL: enable detailed lock wait logging
-- postgresql.conf or ALTER SYSTEM
ALTER SYSTEM SET log_lock_waits = on;
ALTER SYSTEM SET deadlock_timeout = '1s';
SELECT pg_reload_conf();

-- MySQL/InnoDB: inspect the most recent deadlock directly
SHOW ENGINE INNODB STATUS\G
-- Look for the "LATEST DETECTED DEADLOCK" section in the output

9. Deadlock-Strategien im direkten Vergleich

Die folgende Übersicht stellt typische Anti-Patterns den empfohlenen Deadlock-Vermeidung-Strategien gegenüber. Keine der Maßnahmen ist für sich allein ausreichend, aber in Kombination reduzieren sie die Deadlock-Rate in Produktionssystemen erfahrungsgemäß um mehr als 90 Prozent.

Situation Anti-Pattern Empfohlene Strategie Effekt
Mehrere Zeilen sperren Reihenfolge aus Request-Parametern Consistent Lock Ordering nach ID Verhindert zyklische Wartebeziehungen
Externe API-Aufrufe Innerhalb der Transaktion aufrufen Außerhalb, vor Transaktionsbeginn Reduziert Sperrdauer drastisch
Deadlock-Fehler Request sofort fehlschlagen lassen Retry-with-Backoff und Jitter Unmerkliche Wiederholung statt Fehler
Referenzprüfung ohne Index Volle Tabellensperre bei Scan Index auf Fremdschlüsselspalten Nur relevante Zeilen werden gesperrt
Batch-Updates in Schleife Viele einzelne UPDATE-Statements Ein Batch-Statement pro Transaktion Kürzere Sperrdauer insgesamt

Die Tabelle zeigt: Die meisten wirksamen Maßnahmen zur Deadlock-Vermeidung liegen im Anwendungscode, nicht in der Datenbankkonfiguration. Die Datenbank kann Deadlocks erkennen und auflösen, aber nur der Anwendungscode kann sie durch bewusste Reihenfolge und kurze Transaktionen von vornherein vermeiden.

Ein Team, das diese Tabelle als Checkliste für Code-Reviews nutzt, findet die meisten potenziellen Deadlock-Quellen bereits vor dem Merge, statt sie erst über Fehlerberichte aus der Produktion zu entdecken.

Mironsoft

Datenbank-Architektur, Transaktionsdesign und Performance-Tuning

Deadlocks in eurem System, die niemand zuordnen kann?

Wir analysieren eure Transaktionslogik, identifizieren zyklische Sperrmuster und implementieren Lock Ordering, kurze Transaktionsgrenzen und Retry-Strategien, die Deadlocks systematisch verhindern statt sie nur zu protokollieren.

Deadlock-Analyse

Auswertung von Datenbank-Logs zur Identifikation zyklischer Sperrmuster

Transaktions-Refactoring

Lock Ordering, kurze Transaktionsgrenzen und Index-Optimierung umsetzen

Retry-Infrastruktur

Retry-with-Backoff-Patterns in eurer bestehenden Anwendungsschicht verankern

10. Zusammenfassung

Die wirksamste Deadlock-Vermeidung beginnt im Anwendungscode, nicht in der Datenbankkonfiguration. Consistent Lock Ordering sorgt dafür, dass alle Transaktionen dieselben Ressourcen immer in derselben Reihenfolge sperren, wodurch zyklische Wartebeziehungen strukturell unmöglich werden. Kurze Transaktionen minimieren das Zeitfenster, in dem Sperren gehalten werden, indem externe Aufrufe konsequent außerhalb der Transaktionsgrenzen ausgeführt werden. Ein sauberes Retry-with-Backoff-Pattern fängt die verbleibenden, unvermeidbaren Deadlocks ab und macht sie für Endnutzer unsichtbar.

Kein einzelnes Muster reicht für sich genommen aus, erst das Zusammenspiel aus Reihenfolge, Dauer, Fehlerbehandlung und Indexierung macht ein System robust gegen Deadlocks unter realer Produktionslast.

Ergänzend reduzieren passende Indizes auf allen Spalten, die in transaktionalen WHERE-Klauseln verwendet werden, die Anzahl der tatsächlich gesperrten Zeilen, und systematisches Monitoring macht wiederkehrende Deadlock-Muster sichtbar, bevor sie zu einem ernsthaften Skalierungsproblem werden. Wer diese vier Strategien konsequent kombiniert, Lock Ordering, kurze Transaktionen, Retry-Logik und Indexierung, reduziert die Deadlock-Rate in produktiven Systemen erfahrungsgemäß auf ein vernachlässigbares Niveau.

Am Ende bleibt Deadlock-Vermeidung ein fortlaufender Prozess, kein einmaliges Projekt. Neue Features fügen neue Codepfade hinzu, die potenziell neue Lock-Kombinationen erzeugen, und ohne kontinuierliches Monitoring bleiben solche Regressionen oft lange unbemerkt. Ein Team, das Lock Ordering als festen Bestandteil des Code-Reviews etabliert und Deadlock-Metriken genauso ernst nimmt wie Fehlerraten oder Latenzen, verhindert, dass sich fragile Sperrmuster über Zeit unkontrolliert in der Codebasis ausbreiten.

Deadlock-Vermeidung im Anwendungscode: Das Wichtigste auf einen Blick

Lock Ordering

Ressourcen immer in derselben, festen Reihenfolge sperren, zum Beispiel aufsteigend nach Primärschlüssel. Verhindert zyklische Wartebeziehungen strukturell.

Kurze Transaktionen

Externe Aufrufe und Berechnungen außerhalb der Transaktionsgrenzen ausführen. Sperrdauer im Millisekundenbereich statt Sekundenbereich halten.

Retry-with-Backoff

Deadlock-Fehlercode explizit abfangen und mit exponentiellem Backoff plus Jitter erneut ausführen. Nicht pauschal für alle Fehlerarten.

Monitoring

Deadlock-Logs regelmäßig auswerten, Muster nach Tabellen und Zeitpunkt identifizieren, Deadlock-Rate als Metrik im Dashboard verfolgen.

11. FAQ: Deadlock-Vermeidung im Anwendungscode

1Unterschied Deadlock vs. Lock Contention?
Bei Lock Contention wartet eine Transaktion und kann danach fortfahren. Bei einem Deadlock bilden die Wartebeziehungen einen Zyklus ohne Ausweg ohne aktives Eingreifen der Datenbank.
2Wie funktioniert Consistent Lock Ordering?
Ressourcen immer in fester Reihenfolge sperren, zum Beispiel aufsteigend nach Primärschlüssel. Verhindert zyklische Wartebeziehungen strukturell.
3Warum keine externen API-Aufrufe in Transaktionen?
Wartezeit auf externe Antworten hält Sperren länger, das erhöht die Sperrdauer drastisch und damit das Deadlock-Risiko.
4Welcher Fehlercode meldet einen Deadlock?
PostgreSQL: SQLSTATE 40P01. MySQL/InnoDB: Fehlercode 1213. Explizit abfangen und von anderen Fehlern unterscheiden.
5Warum ist Jitter beim Retry wichtig?
Ohne Jitter treffen kollidierte Transaktionen oft synchron wieder aufeinander. Zufällige Verzögerung entkoppelt die Retry-Versuche zeitlich.
6Beeinflusst das Isolation Level die Deadlock-Rate?
Ja. REPEATABLE READ hält zusätzliche Range-Sperren, SERIALIZABLE erhöht die Zahl der Serialization-Failures, die ebenfalls Retries brauchen.
7Wie erkennt die Datenbank einen Deadlock?
Über einen Wait-for-Graph. Findet der Detector einen Zyklus, wählt er eine Transaktion als Opfer und bricht sie per Rollback ab.
8Warum reduziert ein Index Deadlocks?
Ohne Index werden bei Referenzprüfungen mehr Zeilen gescannt und gesperrt als nötig. Ein Index begrenzt Sperren auf relevante Zeilen.
9Retry bei jedem SQL-Fehler auslösen?
Nein. Nur transiente Fehler wie Deadlocks rechtfertigen automatischen Retry. Constraint-Verletzungen sofort zurückgeben.
10Wie überwacht man Deadlocks in Produktion?
log_lock_waits in PostgreSQL, SHOW ENGINE INNODB STATUS in MySQL, Extended Events in SQL Server. Dashboard mit Deadlock-Rate pro Stunde führen.