Distributed Transactions und Two-Phase Commit
AI generated
SELECT
JOIN
SQL · Verteilte Systeme · Transaktionen
Distributed Transactions und Two-Phase Commit
vom 2PC-Protokoll zur Saga als Alternative

Sobald eine Transaktion mehrere unabhängige Datenbanken oder Services betrifft, reicht ein einfaches COMMIT nicht mehr aus, um Atomarität zu garantieren. Two-Phase Commit löst dieses Problem über ein Koordinator-basiertes Prepare-und-Commit-Protokoll, hat aber strukturelle Schwächen, die in großen, verteilten Systemen heute meist den Saga Pattern-Ansatz mit kompensierenden Aktionen bevorzugen lassen.

16 Min. Lesezeit 2PC · XA · Saga Pattern PostgreSQL · MySQL · Verteilte Systeme

1. Was Distributed Transactions sind und warum man sie braucht

Eine Distributed Transaction koordiniert Änderungen über mehrere unabhängige, transaktionale Ressourcen hinweg, typischerweise mehrere Datenbanken, Message Queues oder Services, sodass alle beteiligten Ressourcen entweder gemeinsam committen oder gemeinsam zurückrollen. Eine gewöhnliche lokale Transaktion garantiert Atomarität nur innerhalb einer einzigen Datenbankverbindung, sobald zwei getrennte Datenbanken beteiligt sind, gibt es kein natives COMMIT, das beide gleichzeitig und atomar abschließt.

Der Bedarf an Distributed Transactions entsteht typischerweise in Architekturen mit mehreren separaten Datenbanksystemen, etwa wenn ein Bestellsystem in einer Datenbank und ein Buchhaltungssystem in einer anderen Datenbank gleichzeitig konsistent aktualisiert werden müssen. Ohne Koordination könnte eine Bestellung erfolgreich angelegt werden, während die zugehörige Buchung in der Buchhaltung fehlschlägt, was inkonsistente Zustände zwischen den beiden Systemen hinterlässt, die manuell korrigiert werden müssten.

Das klassische Protokoll zur Lösung dieses Problems ist Two-Phase Commit, kurz 2PC, ein Konsensprotokoll, das einen zentralen Koordinator nutzt, um alle beteiligten Ressourcen in zwei aufeinanderfolgenden Phasen zu einem gemeinsamen Ergebnis zu bringen. 2PC ist mathematisch elegant und garantiert unter bestimmten Annahmen echte Atomarität über Systemgrenzen hinweg, hat aber praktische Eigenschaften, die seinen Einsatz im großen Maßstab erheblich einschränken.

2. Das 2PC-Protokoll: Prepare Phase

Die erste Phase von Two-Phase Commit heißt Prepare Phase oder Voting Phase. Der Koordinator sendet an jeden beteiligten Teilnehmer, in der 2PC-Terminologie als Resource Manager bezeichnet, die Anfrage, ob er bereit ist, seine lokale Transaktion zu committen. Jeder Teilnehmer führt daraufhin alle nötigen Prüfungen durch, Constraints, Sperrbarkeit der betroffenen Ressourcen, Speicherplatz, und schreibt bei positivem Ergebnis seine Änderungen bereits vollständig in ein dauerhaftes Log, ohne sie jedoch final sichtbar zu machen. Anschließend antwortet er dem Koordinator mit Yes, ich bin bereit, oder No, ich kann nicht committen.

Entscheidend an dieser Phase ist, dass ein Teilnehmer, der mit Yes geantwortet hat, sich verpflichtet, seine vorbereiteten Änderungen später auf Anweisung tatsächlich zu committen, egal was danach passiert, selbst wenn seine Verbindung zum Koordinator zwischenzeitlich abbricht. Diese Verpflichtung ist der Kern, der 2PC seine Atomaritätsgarantie verleiht, sie ist aber auch die Quelle der später beschriebenen Blocking-Problematik.


-- Standard XA syntax across participating databases (conceptual)
-- Coordinator instructs each resource manager to prepare its branch

-- Resource manager A (orders database)
XA START 'tx-42:branch-a';
UPDATE orders SET status = 'confirmed' WHERE id = 1001;
XA END 'tx-42:branch-a';
XA PREPARE 'tx-42:branch-a';
-- Changes are durably logged but not yet visible, branch A votes "ready"

-- Resource manager B (accounting database)
XA START 'tx-42:branch-b';
INSERT INTO ledger_entries (order_id, amount) VALUES (1001, 249.00);
XA END 'tx-42:branch-b';
XA PREPARE 'tx-42:branch-b';
-- Changes are durably logged but not yet visible, branch B votes "ready"

-- Coordinator now waits for votes from all branches before proceeding

3. Das 2PC-Protokoll: Commit Phase

Erst wenn der Koordinator von allen Teilnehmern ein positives Votum erhalten hat, geht das Protokoll in die zweite Phase über, die Commit Phase. Der Koordinator schreibt zunächst selbst eine Entscheidung, meist Commit, in sein eigenes dauerhaftes Log, dieser Moment gilt als der eigentliche Commit-Punkt der gesamten verteilten Transaktion. Danach sendet er an jeden Teilnehmer die Anweisung, seine vorbereitete Transaktion final zu committen, und jeder Teilnehmer macht seine Änderungen daraufhin sichtbar und bestätigt dem Koordinator den Abschluss.

Hat auch nur ein einziger Teilnehmer in der Prepare Phase mit No geantwortet, oder ist einer nicht rechtzeitig erreichbar, entscheidet sich der Koordinator stattdessen für Abort und weist alle Teilnehmer an, ihre vorbereiteten Änderungen zu verwerfen. Dieses Alles-oder-nichts-Verhalten ist genau die Atomaritätsgarantie, die Distributed Transactions versprechen: entweder committen alle beteiligten Systeme gemeinsam, oder keines von ihnen tut es.


-- Coordinator received "ready" from both branch A and branch B, proceeds to commit

-- Resource manager A
XA COMMIT 'tx-42:branch-a';
-- Order status change becomes visible

-- Resource manager B
XA COMMIT 'tx-42:branch-b';
-- Ledger entry becomes visible

-- If either branch had voted "no" in the prepare phase, the coordinator
-- would issue XA ROLLBACK to both branches instead, discarding all changes

4. XA Transactions als Standard

XA ist der von der Open Group standardisierte Industriestandard für Distributed Transactions, der definiert, wie ein Transaktions-Koordinator mit mehreren Resource Managern über eine einheitliche Schnittstelle kommuniziert. Nahezu alle relationalen Datenbanken, PostgreSQL, MySQL, Oracle, SQL Server, unterstützen XA-Transaktionen, ebenso viele Message-Queue-Systeme, was es theoretisch möglich macht, Datenbank-Operationen und Nachrichten-Versand in einer einzigen atomaren Distributed Transaction zu koordinieren.

Der Koordinator selbst wird in der Praxis meist von einem Application Server oder einem dedizierten Transaction Manager übernommen, nicht von den Datenbanken selbst, die lediglich die Rolle des Resource Managers ausfüllen. Diese Trennung erlaubt es, XA-fähige Ressourcen unterschiedlichster Art in ein und derselben verteilten Transaktion zu koordinieren, verlangt aber auch, dass der Koordinator selbst hochverfügbar und absturzsicher implementiert ist, denn er ist der Single Point of Truth für die endgültige Commit-Entscheidung.

5. Failure Modes: Koordinator-Ausfall und Blocking Problem

Das strukturelle Problem von Two-Phase Commit zeigt sich in genau dem Moment, den die Prepare Phase erzwingt: Ein Teilnehmer, der bereits mit Yes geantwortet hat, muss seine Sperren auf den betroffenen Ressourcen halten, bis er die endgültige Entscheidung des Koordinators erhält. Fällt der Koordinator nach dem Einsammeln der Stimmen aus, bevor er seine Entscheidung an alle Teilnehmer verteilt hat, bleiben diese Teilnehmer in einem unentschiedenen Zustand hängen, ihre Sperren bleiben aktiv, und alle anderen Transaktionen, die dieselben Ressourcen benötigen, werden ebenfalls blockiert.

Dieses sogenannte Blocking Problem ist keine Implementierungsschwäche, sondern eine strukturelle Eigenschaft des 2PC-Protokolls selbst: Ein Teilnehmer kann nicht eigenständig entscheiden, ob er committen oder zurückrollen soll, weil er nicht weiß, wie die anderen Teilnehmer abgestimmt haben. Erst wenn der Koordinator wieder verfügbar ist oder ein manueller Eingriff erfolgt, können die blockierten Ressourcen freigegeben werden. In Produktionsumgebungen bedeutet ein Koordinator-Ausfall während einer laufenden 2PC-Transaktion daher potenziell minutenlange oder sogar stundenlange Sperrzeiten auf kritischen Ressourcen, bis der Zustand manuell aufgelöst wird.


-- After a coordinator crash, a prepared but undecided branch survives a restart
-- and must be resolved manually or through recovery tooling

-- List all prepared transactions still awaiting a final decision
SELECT gid, prepared, owner, database FROM pg_prepared_xacts;

-- Manual resolution once the correct outcome is known from coordinator logs
COMMIT PREPARED 'tx-42:branch-a';
-- or, if the coordinator's log shows an abort decision:
-- ROLLBACK PREPARED 'tx-42:branch-a';
-- Until this runs, the branch keeps holding its locks indefinitely

6. Warum 2PC selten im großen Maßstab genutzt wird

Two-Phase Commit skaliert schlecht, weil jede beteiligte Ressource während der gesamten Dauer des Protokolls Sperren hält, von der Prepare Phase bis zur endgültigen Commit-Bestätigung. Je mehr Teilnehmer an einer verteilten Transaktion beteiligt sind und je höher die Netzwerklatenz zwischen ihnen ist, desto länger dauert der gesamte Vorgang und desto länger bleiben Ressourcen für andere Transaktionen blockiert. In Systemen mit hoher Nebenläufigkeit und geografisch verteilten Datenbanken wird dieser Effekt schnell zum limitierenden Faktor für den Gesamtdurchsatz.

Hinzu kommt die operative Komplexität: Ein XA-Koordinator muss selbst hochverfügbar sein, sein Entscheidungslog muss dauerhaft und absturzsicher persistiert werden, und im Fehlerfall braucht es Recovery-Mechanismen, die verwaiste, unentschiedene Transaktionen erkennen und auflösen können. Cloud-native Datenbanken und moderne verteilte Architekturen mit Microservices verzichten deshalb häufig bewusst auf 2PC, weil die Kombination aus Blocking-Risiko, Latenz-Overhead und operativer Komplexität den Nutzen echter Atomarität in den meisten Geschäftsanwendungen nicht mehr rechtfertigt. Genau hier setzt das Saga Pattern als pragmatischere Alternative an.

7. Saga Pattern als Alternative

Das Saga Pattern verzichtet bewusst auf die strikte Atomaritätsgarantie von Distributed Transactions und ersetzt sie durch eine Sequenz lokaler Transaktionen, von denen jede unabhängig committet, kombiniert mit kompensierenden Aktionen, die bei einem Fehler die bereits erfolgreich abgeschlossenen Schritte gezielt rückgängig machen. Statt eines globalen Koordinators, der Sperren über alle Teilnehmer hinweg hält, läuft jeder Schritt einer Saga als eigenständige, kurze lokale Transaktion, die sofort committet und ihre Sperren sofort wieder freigibt.

Schlägt ein Schritt fehl, ruft die Saga für jeden bereits erfolgreich abgeschlossenen vorherigen Schritt die passende Kompensationsaktion auf, etwa eine Stornobuchung statt eines echten Rollbacks. Für die Bestellung aus dem vorherigen Beispiel würde das bedeuten: Schlägt die Buchung in der Buchhaltung fehl, storniert eine Kompensationsaktion die bereits angelegte Bestellung, statt zu versuchen, eine globale Transaktion über beide Systeme rückgängig zu machen. Der Vorteil: keine langlebigen Sperren, keine Abhängigkeit von einem hochverfügbaren globalen Koordinator, deutlich bessere Skalierbarkeit über Servicegrenzen hinweg.


-- Saga pattern: each step is its own short, independently committed local transaction
-- Pseudocode orchestration around per-service local transactions

-- Step 1: orders service, its own local transaction, commits immediately
BEGIN;
INSERT INTO orders (id, status) VALUES (1001, 'pending');
COMMIT;

-- Step 2: accounting service, its own local transaction, commits immediately
BEGIN;
INSERT INTO ledger_entries (order_id, amount) VALUES (1001, 249.00);
COMMIT;
-- Suppose this step fails due to a business rule violation

-- Compensating action instead of a distributed rollback:
-- undo step 1 with its own local transaction
BEGIN;
UPDATE orders SET status = 'cancelled' WHERE id = 1001;
COMMIT;
-- No global lock was ever held across both services during this process

8. Vergleich: 2PC vs. Saga

Der fundamentale Unterschied zwischen Two-Phase Commit und dem Saga Pattern liegt im Konsistenzmodell: 2PC garantiert starke, sofortige Konsistenz über alle beteiligten Systeme hinweg, um den Preis von Sperren, Latenz und Blocking-Risiko. Saga liefert Eventual Consistency, die Systeme sind zwischenzeitlich in einem inkonsistenten Zwischenzustand sichtbar, etwa eine Bestellung ohne zugehörige Buchung für einen kurzen Moment, konvergieren aber garantiert entweder zu einem vollständig erfolgreichen oder einem vollständig kompensierten Endzustand.

Diese Eventual Consistency erfordert vom Anwendungscode mehr Sorgfalt: Jeder Zwischenzustand muss fachlich vertretbar sein, und für jeden Schritt muss eine korrekte, idempotente Kompensationsaktion existieren. Dafür entfällt die operative Komplexität eines hochverfügbaren XA-Koordinators vollständig, und die Gesamt-Skalierbarkeit steigt erheblich, weil keine Ressource länger als die Dauer ihrer eigenen kurzen lokalen Transaktion gesperrt bleibt.


-- Idempotent compensating action: safe to execute more than once
-- Using a status check prevents double-cancellation from a retried saga step
UPDATE orders
SET status = 'cancelled'
WHERE id = 1001
  AND status <> 'cancelled';  -- no-op if already compensated, safe to retry

-- Idempotent forward step, guarded by a unique constraint on order_id
INSERT INTO ledger_entries (order_id, amount)
SELECT 1001, 249.00
WHERE NOT EXISTS (
  SELECT 1 FROM ledger_entries WHERE order_id = 1001
);

9. Distributed-Transaction-Strategien im direkten Vergleich

Die folgende Tabelle fasst die wichtigsten Unterschiede zwischen Two-Phase Commit und dem Saga Pattern zusammen und zeigt, wann welcher Ansatz die richtige Wahl ist.

Kriterium Two-Phase Commit Saga Pattern Konsequenz
Konsistenzmodell Starke, sofortige Konsistenz Eventual Consistency Saga braucht fachlich tolerable Zwischenzustände
Sperrdauer Über die gesamte Protokolldauer Nur pro einzelner lokaler Transaktion Saga skaliert deutlich besser
Fehlerbehandlung Automatisches Rollback aller Teilnehmer Manuell definierte Kompensationsaktionen Saga erfordert mehr Entwicklungsaufwand
Koordinator-Ausfall Blocking Problem, Ressourcen bleiben gesperrt Kein globaler Koordinator nötig Saga vermeidet Single Point of Failure
Typischer Einsatz Wenige, stabile, homogene Systeme Viele, heterogene Microservices Wahl hängt von Systemlandschaft ab

Die Tabelle macht deutlich: Two-Phase Commit ist nicht grundsätzlich veraltet, aber in modernen, verteilten Microservice-Landschaften mit vielen Teilnehmern und variabler Netzwerklatenz überwiegen die strukturellen Nachteile meist die Vorteile starker Konsistenz.

Mironsoft

Verteilte Systeme, Transaktionsarchitektur und Microservice-Konsistenz

Konsistenz zwischen mehreren Systemen ohne globale Sperren?

Wir analysieren eure verteilte Architektur, bewerten, ob 2PC oder Saga Pattern zu eurer Systemlandschaft passt, und implementieren robuste Kompensationslogik für konsistente Prozesse über Servicegrenzen hinweg.

Architektur-Review

Bewertung eurer verteilten Transaktionsanforderungen und Systemgrenzen

Saga-Implementierung

Orchestrierung und Kompensationslogik für Microservice-Prozesse aufbauen

XA-Beratung

Wo starke Konsistenz wirklich nötig ist, XA-Transaktionen sauber einrichten

10. Zusammenfassung

Distributed Transactions lösen ein grundlegendes Problem verteilter Systeme: Atomarität über mehrere unabhängige Ressourcen hinweg zu garantieren. Two-Phase Commit erreicht das über einen Koordinator, der in einer Prepare Phase Zustimmung von allen Teilnehmern einholt und erst danach in der Commit Phase final committet. Das Protokoll ist mathematisch korrekt, hat aber ein strukturelles Blocking Problem bei Koordinator-Ausfall und skaliert wegen langer Sperrzeiten schlecht in Systemen mit vielen Teilnehmern oder hoher Latenz.

Das Saga Pattern verzichtet auf starke, sofortige Konsistenz zugunsten von Eventual Consistency und ersetzt den globalen Koordinator durch eine Sequenz kurzer, unabhängig committender lokaler Transaktionen mit definierten Kompensationsaktionen für den Fehlerfall. In modernen Microservice-Architekturen mit vielen heterogenen Systemen ist diese pragmatischere Alternative meist die bessere Wahl, während 2PC in kleineren, stabileren Systemlandschaften mit wenigen Teilnehmern weiterhin seine Berechtigung behält.

Distributed Transactions und Two-Phase Commit: Das Wichtigste auf einen Blick

2PC-Protokoll

Prepare Phase sammelt Zustimmung aller Teilnehmer, Commit Phase führt die Entscheidung final aus, alles oder nichts.

Blocking Problem

Fällt der Koordinator nach der Prepare Phase aus, bleiben Teilnehmer mit gehaltenen Sperren in unentschiedenem Zustand hängen.

Saga Pattern

Kurze, unabhängig committende lokale Transaktionen plus Kompensationsaktionen statt globaler Sperren.

Entscheidungskriterium

Wenige, stabile Systeme: 2PC vertretbar. Viele heterogene Microservices: Saga meist die bessere Wahl.

11. FAQ: Distributed Transactions und Two-Phase Commit

1Was ist eine Distributed Transaction?
Koordiniert Änderungen über mehrere unabhängige Ressourcen, sodass alle gemeinsam committen oder gemeinsam zurückrollen.
2Was passiert in der Prepare Phase?
Koordinator fragt alle Teilnehmer, jeder prüft und loggt seine Änderungen, antwortet mit Yes oder No.
3Was passiert in der Commit Phase?
Bei einstimmigem Yes committen alle final, bei einem No rollen alle zurück.
4Was ist XA?
Der Open-Group-Standard für die Kommunikation zwischen Koordinator und mehreren Resource Managern.
5Was ist das Blocking Problem?
Koordinator-Ausfall nach der Prepare Phase lässt Teilnehmer mit Sperren unentschieden hängen.
6Warum skaliert 2PC schlecht?
Sperren über die gesamte Protokolldauer, mehr Teilnehmer und Latenz verschärfen das Problem.
7Was ist das Saga Pattern?
Sequenz unabhängiger lokaler Transaktionen plus Kompensationsaktionen statt globaler Transaktion.
8Starke Konsistenz vs. Eventual Consistency?
Starke Konsistenz: immer konsistent. Eventual Consistency: zwischenzeitlich inkonsistent, garantiert am Ende konvergent.
9Wann ist 2PC noch sinnvoll?
Wenige, stabile Systeme, niedrige Latenz, zwingende starke Konsistenzanforderung.
10Was braucht eine korrekte Kompensationsaktion?
Idempotenz und fachlich korrektes Rückgängigmachen des ursprünglichen Schritts.