Anomalien bei Transaktionen:Dirty Read, Non-Repeatable Read, Phantom Read
Lass uns nochmal die Transaktionstypen durchgehen! In der Welt der Datenbank-Transaktionen gibt es drei Haupt-"Bösewichte", die dir echt den Tag versauen können: Dirty Read, Non-Repeatable Read und Phantom Read. Diese "Anomalien" tauchen auf, wenn das Isolation Level der Transaktionen nicht ausreicht. Heute schauen wir uns an, wer diese Bösewichte sind, wie sie sich bemerkbar machen und – ganz wichtig – wie du sie in den Griff bekommst.
Bevor wir zu den Beispielen kommen, lass uns kurz klären, was sie bedeuten.
Dirty Read (Schmutziges Lesen):
Du liest Daten, die zwar geändert wurden, aber die Transaktion, die sie geändert hat, ist noch nicht bestätigt (COMMIT) oder – noch schlimmer – könnte zurückgerollt werden (ROLLBACK). Das ist so, als würdest du Geld an einen Freund schicken, deinen Kontostand checken und sehen, dass du pleite bist, aber dann überlegst du es dir anders und holst das Geld zurück. Magisch!Non-Repeatable Read (Nicht wiederholbares Lesen):
Du liest dieselben Daten zweimal innerhalb einer Transaktion, aber zwischen den beiden Lesevorgängen wurden die Daten von einer anderen Transaktion geändert, und du bekommst zwei verschiedene Ergebnisse. Das ist wie wenn du das Geburtsdatum in deinem Ausweis anschaust, ihn dann einem Freund gibst, der die Zahlen ändert, und du nochmal nachschaust – plötzlich hast du ein neues Geburtsdatum.Phantom Read (Phantom-Lesen):
Du führst denselben Query zweimal aus, aber beim zweiten Mal siehst du zusätzliche Zeilen, die von einer anderen Transaktion eingefügt wurden. Das ist wie wenn du die Leute im Raum zählst, und jemand bringt heimlich noch Freunde mit rein.
Problem Dirty Read
Angenommen, wir haben eine Tabelle accounts:
CREATE TABLE accounts (
account_id SERIAL PRIMARY KEY,
owner TEXT NOT NULL,
balance NUMERIC(10, 2) NOT NULL
);
INSERT INTO accounts (owner, balance) VALUES ('Alice', 1000), ('Bob', 500);
Transaktion 1 ändert das Guthaben, ist aber noch nicht abgeschlossen, während Transaktion 2 versucht, diese Daten gleichzeitig zu lesen.
Transaktion 1:
BEGIN;
UPDATE accounts SET balance = balance - 200 WHERE owner = 'Alice';
-- Guthaben von Alice ist jetzt 800, aber die Transaktion ist noch nicht abgeschlossen.
Transaktion 2:
BEGIN;
SELECT balance FROM accounts WHERE owner = 'Alice'; -- Wir sehen Guthaben: 800 (schmutziges Lesen).
ROLLBACK; -- Transaktion 1 wird zurückgerollt.
Jetzt arbeitet Transaktion 2 mit falschen Daten, weil Transaktion 1 die Änderungen zurückgenommen hat. Das kannst du vermeiden, wenn du das Isolation Level READ COMMITTED nutzt, das verhindert, dass du Änderungen von nicht bestätigten Transaktionen siehst.
Problem Non-Repeatable Read
Stell dir vor, Transaktion 1 liest Daten, eine andere Transaktion ändert sie, und Transaktion 1 liest die Daten nochmal. Jetzt sind sie anders.
Transaktion 1:
BEGIN;
SELECT balance FROM accounts WHERE owner = 'Bob'; -- Wir sehen Guthaben: 500.
Transaktion 2 parallel:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE owner = 'Bob';
COMMIT;
Transaktion 1 nochmal:
SELECT balance FROM accounts WHERE owner = 'Bob'; -- Wir sehen Guthaben: 400.
COMMIT;
Siehst du, wie sich die Daten innerhalb einer Transaktion geändert haben? In echten Szenarien kann das kritisch sein, zum Beispiel bei Finanzberichten. Das Problem löst du mit einem höheren Isolation Level, wie REPEATABLE READ.
Problem Phantom Read
Angenommen, wir haben eine Tabelle orders:
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer TEXT NOT NULL,
total NUMERIC(10, 2) NOT NULL
);
INSERT INTO orders (customer, total) VALUES ('Alice', 100), ('Bob', 200);
Transaktion 1 zählt die Anzahl der Bestellungen, eine andere Transaktion fügt eine neue Bestellung hinzu, und Transaktion 1 zählt nochmal.
Transaktion 1:
BEGIN;
SELECT COUNT(*) FROM orders; -- Wir sehen: 2.
Transaktion 2 parallel:
BEGIN;
INSERT INTO orders (customer, total) VALUES ('Charlie', 300);
COMMIT;
Transaktion 1 nochmal:
SELECT COUNT(*) FROM orders; -- Wir sehen: 3. Eine neue "Phantom"-Bestellung ist aufgetaucht!
COMMIT;
Um Phantom Reads zu verhindern, brauchst du das Isolation Level SERIALIZABLE, das parallele Änderungen, die das Ergebnis beeinflussen, komplett blockiert.
Wie kann man Anomalien verhindern?
Isolation Levels vs. Anomalien
| Isolation Level | Dirty Read |
Non-Repeatable Read |
Phantom Read |
|---|---|---|---|
Read Uncommitted |
❌ Ja | ❌ Ja | ❌ Ja |
Read Committed |
✅ Nein | ❌ Ja | ❌ Ja |
Repeatable Read |
✅ Nein | ✅ Nein | ❌ Ja |
Serializable |
✅ Nein | ✅ Nein | ✅ Nein |
Wie wählst du das richtige Isolation Level?
- Wenn du schnellen Zugriff auf Daten brauchst und Anomalien nicht kritisch sind (z.B. Analytics auf alten Daten) – nimm
READ COMMITTED. - Wenn dir Konsistenz innerhalb einer Transaktion wichtig ist – nimm
REPEATABLE READ. - Wenn du maximale Isolation und Konsistenz brauchst – nimm
SERIALIZABLE. Denk dran: Die Performance kann darunter leiden.
Praxistipps
Nutze Transaktionen mit dem Isolation Level, das zu deinem Use Case passt. Zum Beispiel:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
SELECT ...;
COMMIT;
Füge Indizes hinzu, um Table Locks zu minimieren und Queries schneller zu machen.
Schreibe optimierte Queries, um lange Sperren zu vermeiden – besonders bei SERIALIZABLE.
Spezielle Fehler und wie du sie vermeidest
Manchmal führt ein falsch gewähltes Isolation Level zu Konflikten und schlechter Performance. Zum Beispiel kann SERIALIZABLE in einem System mit vielen parallelen Transaktionen zu Locks und "Hungern" von Transaktionen führen.
Um das zu vermeiden, analysiere deine Queries, teste die Performance bei verschiedenen Isolation Levels und nutze passende Indizes.
GO TO FULL VERSION