Manchmal erinnern Transaktionen an Charaktere aus Superheldenfilmen. Sie retten unsere Datenbanken vor Katastrophen bei Ausfällen, Fehlern oder Störungen. Wenn du an einer Aufgabe arbeitest, die mehrere Operationen erfordert, die nicht getrennt werden dürfen, sorgen Transaktionen dafür, dass alles als Einheit ausgeführt wird. Lass uns anschauen, wie das am Beispiel der Zahlungsabwicklung funktioniert.
Zahlungsabwicklung
Stell dir die klassische Situation vor: Du hast zwei Bankkonten und möchtest Geld von einem auf das andere überweisen. Das ist keine "One-Click"-Operation. Du musst sicherstellen, dass das Geld korrekt vom einen Konto abgebucht und dem anderen gutgeschrieben wird. Jeder Fehler kann zur Katastrophe führen: Entweder bleiben beide Konten unverändert oder das Gleichgewicht wird gestört (zum Beispiel verschwindet Geld oder taucht "aus dem Nichts" auf).
Szenario: Geldüberweisung zwischen Konten
Hier ist unser Code. Lies ihn aufmerksam, als wäre es eine Nachricht aus einer weit entfernten Galaxie:
-- Transaktion starten
BEGIN;
-- Schritt 1. Geld vom Konto des Absenders abbuchen
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
-- Schritt 2. Geld auf das Konto des Empfängers gutschreiben
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;
-- Alles lief gut? Dann Änderungen speichern!
COMMIT;
Was ist hier wichtig?
- Wenn bei
Schritt 1oderSchritt 2etwas schiefgeht (zum Beispiel ein Fehler in der Abfrage, zu wenig Guthaben), kann die Transaktion zurückgerollt werden mitROLLBACK, und die Daten bleiben im Ursprungszustand. COMMITgarantiert, dass die Änderungen nur übernommen werden, wenn ALLE Schritte erfolgreich sind.
Balance-Check hinzufügen
Und was, wenn der Absender nicht genug Geld für die Überweisung hat? Lass uns einen Balance-Check einbauen, damit er nicht versehentlich ins Minus rutscht.
-- Transaktion starten
BEGIN;
-- Aktuelles Guthaben des Absenders abfragen
DO $$
DECLARE
current_balance NUMERIC;
BEGIN
SELECT balance INTO current_balance FROM accounts WHERE account_id = 1;
-- Prüfen, ob genug Geld da ist
IF current_balance >= 100 THEN
-- Wenn genug Geld da ist, Überweisung ausführen
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;
-- Änderungen festschreiben
COMMIT;
ELSE
-- Wenn nicht genug Geld da ist, zurückrollen
ROLLBACK;
RAISE NOTICE 'Nicht genug Guthaben für die Überweisung!';
END IF;
END $$;
Was ist hier schon spannender?
- Wir nutzen einen PL/pgSQL-Block mit einer Bedingungsprüfung via
IF. Wenn das Guthaben kleiner als der benötigte Betrag ist, wird die Transaktion abgelehnt und nichts geändert. ROLLBACKmacht Änderungen rückgängig, falls welche gemacht wurden (auch wenn hier noch nichts geändert wurde, ist das trotzdem guter Stil).
Diese Lektion widmet sich echten Szenarien für den Einsatz von Transaktionen, deshalb habe ich hier ein Beispiel aus dem echten Leben genommen. Es enthält eine gespeicherte Prozedur und ist mit PL-SQL geschrieben. Ich denke, du hast schon genug Erfahrung, um zu verstehen, wie das hier funktioniert. In Zukunft kommen wir nochmal auf PL-SQL zurück und schauen uns noch viel komplexere Beispiele an.
Massen-Update von Daten in einer Transaktion
Eine Transaktion zu erstellen ist nicht nur für Geldüberweisungen praktisch. Angenommen, wir haben eine Datenbank eines Online-Shops, in der täglich Dutzende Bestellungen ihren Status ändern, zum Beispiel von "in Lieferung" zu "abgeschlossen". Wie aktualisiert man viele Datensätze auf einmal, sodass man im Fehlerfall alles zurückrollen kann? Natürlich mit einer Transaktion.
Schauen wir uns noch ein Szenario an: Status-Update von Bestellungen.
Hier ein Beispiel:
-- Transaktion starten
BEGIN;
-- Schritt 1. Bestellungen mit vergangenem Lieferdatum aktualisieren
UPDATE orders
SET status = 'abgeschlossen'
WHERE delivery_date < CURRENT_DATE;
-- Schritt 2. Über erfolgreichen Update informieren
RAISE NOTICE 'Alle Bestellstatus wurden erfolgreich aktualisiert.';
-- Änderungen übernehmen
COMMIT;
Und wenn etwas schiefgeht?
Es besteht immer die Möglichkeit eines Fehlers. Zum Beispiel hast du versehentlich die WHERE-Bedingung vergessen, und jetzt haben alle Bestellungen den Status abgeschlossen. Um solche Situationen zu vermeiden, ist es wichtig, die Transaktion zu beenden oder explizit zurückzurollen.
Hier ein Szenario mit Rollback:
-- Transaktion starten
BEGIN;
-- Schritt 1. Versuch, Bestellungen ohne Bedingung zu aktualisieren (oh nein, Fehler!)
UPDATE orders
SET status = 'abgeschlossen';
-- Transaktion wegen Fehler zurückrollen
ROLLBACK;
-- Jetzt sind die Bestellungen unverändert geblieben
Ein bisschen mehr "Flexibilität" mit SAVEPOINT
Man muss nicht immer die ganze Transaktion zurückrollen. Wenn dein Szenario aus mehreren Schritten besteht, willst du vielleicht nur einen Teil zurückrollen. Hier hilft SAVEPOINT.
Jetzt unser Szenario: Mehrere Schritte bearbeiten mit der Möglichkeit, einen davon zurückzurollen.
Stell dir vor, du bearbeitest eine Bestellung in mehreren Schritten: Artikel vom Lager abbuchen, Bestellstatus aktualisieren, Benachrichtigung an den Kunden schicken. Wenn die Benachrichtigung nicht erfolgreich gesendet wurde, willst du nur diesen Schritt zurückrollen, aber die Änderungen in der Datenbank behalten.
-- Transaktion starten
BEGIN;
-- Schritt 1. Artikel vom Lager abbuchen
UPDATE products
SET stock = stock - 1
WHERE product_id = 101;
-- Savepoint setzen
SAVEPOINT step1;
-- Schritt 2. Bestellstatus aktualisieren
UPDATE orders
SET status = 'versendet'
WHERE order_id = 202;
-- Versuch, Benachrichtigung an den Kunden zu senden
SAVEPOINT step2;
-- Ups, Fehler beim Benachrichtigen!
ROLLBACK TO SAVEPOINT step2;
-- Entscheiden, dass es sicher ist, die Transaktion abzuschließen
COMMIT;
Fazit
Transaktionen sind nicht nur ein technisches Werkzeug, sondern eine Garantie für die Integrität deiner Daten. Sie schützen vor dem "Domino-Effekt", bei dem ein Fehler das ganze System zerstören kann. Immer wenn du mehrere zusammenhängende Operationen ausführst, frag dich: "Was, wenn eine davon fehlschlägt?" Wenn die Antwort "Katastrophe" lautet, ist es Zeit für eine Transaktion. Denk dran: Lieber ein paar Minuten in eine Transaktion investieren als Stunden mit der Wiederherstellung von Daten nach einem Crash zu verbringen. Deine User (und deine Nerven) werden es dir danken!
GO TO FULL VERSION