Lass uns mal anschauen, was das Isolationslevel READ COMMITTED eigentlich macht. Der Name verrät schon ein bisschen: Alles, was wir innerhalb einer Transaktion lesen, ist schon von anderen Transaktionen "committed". So wie im echten Leben, wo wir nur an die Gerüchte glauben, die offiziell im Protokoll stehen.
Mal im Ernst: Dieses Level garantiert, dass eine Transaktion keine Änderungen sieht, die von anderen Transaktionen gemacht, aber noch nicht bestätigt wurden. Das löst das Problem, das als "Dirty Read" (Dirty Read) bekannt ist. Aber denk dran: Die Daten, die du liest, können sich ändern, wenn eine andere Transaktion zwischen deinen Abfragen einen Commit macht. Das führt zum Risiko eines "Non-Repeatable Read" (Non-Repeatable Read).
In PostgreSQL ist das Isolationslevel READ COMMITTED der Standard. Das ist quasi der Default-Modus – du musst nichts extra einstellen, um es zu nutzen.
Beispiel für die Nutzung von READ COMMITTED
Schauen wir uns mal an, wie das in der Praxis aussieht. Stell dir vor, wir haben eine Tabelle accounts, die Infos über User und deren Kontostände enthält:
CREATE TABLE accounts (
account_id SERIAL PRIMARY KEY,
account_name TEXT NOT NULL,
balance NUMERIC(10, 2) NOT NULL
);
INSERT INTO accounts (account_name, balance)
VALUES ('Alice', 1000.00), ('Bob', 500.00);
Jetzt stell dir folgende Situation vor. Session 1 und Session 2 – unsere beiden Helden – arbeiten beide mit der Tabelle accounts. Das passiert dabei:
Session 1:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_name = 'Alice';
-- Noch kein COMMIT oder ROLLBACK.
In diesem Moment wurden bei Alice temporär 100 vom balance abgezogen, aber das Ergebnis ist noch nicht festgeschrieben.
Session 2:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SELECT balance FROM accounts WHERE account_name = 'Alice';
Ergebnis: Session 2 sieht Alices Kontostand als 1000.00, weil die Transaktion von Session 1 noch nicht abgeschlossen ist (COMMIT wurde nicht ausgeführt). Das Level READ COMMITTED schützt uns vor "Dirty Read".
Session 1 schließt die Transaktion ab:
COMMIT;
Jetzt ist Alices Kontostand aktualisiert und der Wert 900.00 ist in der Datenbank gespeichert.
Session 2 wiederholt die Abfrage:
SELECT balance FROM accounts WHERE account_name = 'Alice';
Ergebnis: Jetzt sieht Session 2 den aktualisierten Kontostand von Alice – 900.00. Beachte, dass das Ergebnis anders ist als bei der vorherigen Abfrage, die 1000.00 zurückgegeben hat. Das ist das Problem des "Non-Repeatable Read".
Wann solltest du READ COMMITTED verwenden?
Das Isolationslevel READ COMMITTED ist ein Kompromiss zwischen Performance und Konsistenz. Aber es gibt ein paar Szenarien, wo es einfach perfekt passt:
Einfache CRUD-Operationen: Wenn du einfach nur Daten liest oder aktualisierst, ohne komplexe Zusammenhänge.
Update von Datensätzen: Zum Beispiel ein Massen-Update in einer Tabelle, wo es wichtig ist, die bestätigten Änderungen sofort zu sehen.
Transaktionsverarbeitung: Zahlungssysteme, bei denen User nur bestätigte Daten sehen dürfen.
Wenn du aber komplexe analytische Abfragen machst oder mit großen Datenmengen arbeitest, solltest du vielleicht ein anderes Isolationslevel wie REPEATABLE READ in Betracht ziehen.
Vorteile und Nachteile von READ COMMITTED
Das Isolationslevel READ COMMITTED ist so etwas wie der goldene Mittelweg. Es schützt dich vor schmutzigen Daten: Du siehst keine Änderungen, die eine andere Transaktion angefangen, aber noch nicht abgeschlossen hat. Niemand liest also "rohe" Infos, die gleich wieder zurückgerollt werden könnten.
Dieser Modus ist schneller als strengere Levels (REPEATABLE READ oder SERIALIZABLE), weil er keine komplizierten Locks und zusätzlichen Checks braucht. Er ist ziemlich leichtgewichtig und trotzdem zuverlässig – deshalb ist er Standard und passt super für die meisten Alltagsaufgaben.
Auch wenn er Dirty Reads verhindert, schützt READ COMMITTED nicht vor:
- "Non-Repeatable Read" (
Non-Repeatable Read): Der Wert von Daten kann sich ändern, wenn eine andere Transaktion zwischen deinen Abfragen Änderungen macht. - "Phantom Read" (
Phantom Read): Eine andere Transaktion kann Zeilen hinzufügen, die das Ergebnis deiner Abfrage beeinflussen.
Tipps für die Arbeit mit READ COMMITTED
Transaktionen immer abschließen: Vergiss nicht, COMMIT oder ROLLBACK zu benutzen, sonst gibt's Probleme mit Locks.
Check, ob das Isolationslevel reicht: Wenn du garantieren musst, dass sich Daten während der Transaktion nicht ändern, schau dir REPEATABLE READ an.
Nutze Indexe: Das hilft PostgreSQL, Daten schneller zu finden und Änderungen anzuwenden.
Beispiel: Bestellungen bearbeiten
Angenommen, wir haben eine Tabelle orders, in der Bestelldaten gespeichert sind:
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending'
);
INSERT INTO orders (customer_name, status)
VALUES ('Alice', 'pending'), ('Bob', 'pending');
Wir wollen den Status der Bestellungen updaten, die gerade auf "pending" stehen:
BEGIN;
SELECT * FROM orders WHERE status = 'pending';
UPDATE orders SET status = 'completed' WHERE status = 'pending';
COMMIT;
Wenn währenddessen eine andere Transaktion eine neue Bestellung mit Status "pending" hinzufügt und COMMIT ausführt, berücksichtigt unsere Transaktion diese Zeile nicht, weil sie nach dem Start des Lesens hinzugefügt wurde.
Das ist ein Beispiel für "Phantom Read". Wenn du solche Situationen vermeiden willst, musst du SERIALIZABLE verwenden.
Das Isolationslevel READ COMMITTED ist die Standardwahl für die meisten Datenbanken, auch für PostgreSQL. Es schützt vor Dirty Reads und ist damit eine gute Option für die meisten Standard-Operationen. Aber in Szenarien, die strikte Konsistenz verlangen, brauchst du vielleicht ein strengeres Isolationslevel. Die Wahl des Levels hängt von deinen konkreten Aufgaben und Performance-Anforderungen ab.
GO TO FULL VERSION