Inhalt
Wenn Sie schon einmal gesehen haben, wie ein Transaktionssystem unter hoher Last einfriert, kennen Sie die Art von stiller Panik, die einsetzt. Abfragen stapeln sich, die CPU-Auslastung sieht gut aus, die Protokolle scheinen normal, und doch ist alles blockiert. Deadlocks sind heimtückische Probleme, die sich erst zeigen, wenn sie echten Schaden anrichten. Sie beschädigen keine Daten, aber sie töten die Betriebszeit und erschüttern das Vertrauen in das System, was Teams dazu zwingt, hektisch zu reagieren, bevor die Lage eskaliert. Ein Deadlock entsteht, wenn zwei oder mehr Transaktionen in einer Schleife auf die Sperren der jeweils anderen warten. Das System erkennt diesen Zyklus und beendet eine Transaktion, um ihn zu durchbrechen. In Systemen, die viele Online-Transaktionen verarbeiten, führt eine beendete Transaktion oft zu Wiederholungen, Zeitüberschreitungen und einer Kaskade von Fehlern, die von den Nutzern wahrgenommen werden. Deshalb ist die Vermeidung von Deadlocks nicht nur ein nettes Extra für die Leistung; sie ist entscheidend für die Systemresilienz.\n\nExperten, die Hochdurchsatzsysteme entwerfen, sind sich einig, dass Deadlocks meist nicht zufällig sind. Martin Kleppmann weist darauf hin, dass subtile Inkonsistenzen in der Reihenfolge, in der Sperren erworben werden, die meisten Deadlocks verursachen. Pat Helland argumentiert, dass Systeme, die Reihenfolge und Idempotenz als erstklassige Anliegen behandeln, selten anhaltende Deadlocks erleben. Tammy Butow ergänzt, dass viele Unternehmen unterschätzen, wie Wiederholungsstürme durch schlechte Backoff-Strategien Deadlocks verschlimmern. Ihr Rat lässt sich auf drei Dinge zusammenfassen: vorhersehbare Sperrreihenfolge, begrenzte Wiederholungen und ein klares Verständnis darüber, wie Sperren vergeben werden.\n\nWarum treten Deadlocks also auf? Sie sind keine Unfälle. Vier Bedingungen treten meist zusammen auf: gegenseitiger Ausschluss (nur eine Transaktion kann eine Sperre gleichzeitig halten), Halten und Warten (Transaktionen halten Sperren, während sie auf andere warten), keine Präemption (Sperren können nicht zwangsweise entzogen werden) und zirkuläres Warten (jede Transaktion wartet auf eine Sperre, die von einer anderen in einem Zyklus gehalten wird). Relationale Datenbanken haben all diese Bedingungen per Design. Entwickler können zirkuläres Warten größtenteils kontrollieren. Häufige Ursachen sind inkonsistente Sperrreihenfolgen, lang laufende Transaktionen, die Verwendung zu strenger Isolationsebenen und gegnerische Zugriffsmuster bei Verkehrsspitzen. Zum Beispiel können sich zwei Transaktionen deadlocken, wenn eine zuerst Zeile 1 und dann 2 aktualisiert und die andere zuerst 2 und dann 1, und sie sich dabei ungünstig überschneiden.\n\nDer eigentliche Schaden durch Deadlocks ist nicht die einzelne beendete Transaktion, sondern der darauf folgende Wiederholungssturm. Clients versuchen sofort ohne Verzögerung oder Zufall erneut, was dazu führt, dass Wiederholungen immer wieder kollidieren. Das kann sich zu Ausfällen von mehreren Minuten auswachsen. Ein Fall betraf eine Zahlungs-API, bei der ein 80-ms-Deadlock einen fünfminütigen teilweisen Ausfall verursachte, weil die Wiederholungen stark kollidierten. Im großen Maßstab kann schon ein kleiner Prozentsatz an Deadlocks eine Flut von Fehlern auslösen, die Verbindungspools sättigen. Es ist viel einfacher, den Deadlock zu verhindern, als den Sturm danach einzudämmen.\n\nUm Deadlocks zu verhindern, beginnen Sie damit, eine strikte und dokumentierte Sperrreihenfolge durchzusetzen. Jeder Transaktionstyp muss Sperren in derselben Reihenfolge erwerben – nach Primärschlüssel, Ressourcentyp oder Hierarchie. Lassen Sie nicht zu, dass Benutzereingaben oder dynamische Bedingungen diese Reihenfolge spontan ändern, sonst laden Sie Zyklen ein. Wenn bedingte Schreibvorgänge notwendig sind, führen Sie zuerst eine einfache Metadatenabfrage durch, um den korrekten geordneten Pfad auszuwählen.\n\nKurze Transaktionen helfen ebenfalls. Lange Transaktionen verursachen nicht direkt Deadlocks, vergrößern aber das Zeitfenster für Konflikte. Entfernen Sie unnötige Abfragen und teure Operationen wie API-Aufrufe aus Transaktionen. Behandeln Sie Atomizität wie ein Skalpell statt einen großen Hammer – schützen Sie nur, was wirklich transaktionale Garantien benötigt.\n\nVerwenden Sie die richtige Isolationsebene statt der stärksten. Serializable Isolation mag am sichersten erscheinen, bedeutet aber oft mehr Sperren und ein höheres Deadlock-Risiko. Wählen Sie die schwächste Isolationsebene, die dennoch Korrektheit schützt, wie Snapshot Isolation für Lesevorgänge oder Read Committed für Schreibvorgänge mit expliziten Prüfungen. Höhere Isolation ist nicht immer besser – nur restriktiver.\n\nTrotzdem werden einige Deadlocks auftreten. Fügen Sie daher immer exponentielles Backoff mit Jitter zu Wiederholungsschleifen hinzu. Wiederholen Sie nicht sofort nach einem Fehler. Randomisieren Sie Verzögerungen, damit Wiederholungen sich nicht bündeln und erneut kollidieren. Selbst kleiner Jitter reduziert Wiederholungskollisionen drastisch und bewahrt Sie oft davor, von einem kleinen Problem zu einem ausgewachsenen Vorfall zu werden.\n\nVerfolgen Sie schließlich Sperrkonkurrenz als Schlüsselmetrik. Die meisten Teams beobachten die Gesamtabfrage-Latenz, ignorieren aber, wie lange Abfragen auf Sperren warten. Das Messen von Blockierzeiten, Deadlock-Zahlen und den Top-Sperrwarten hilft, Probleme früh zu erkennen, bevor sie Ausfälle verursachen.