PostgreSQL Transaction Isolation Levels: ACID-Konformität verstehen
PostgreSQL implementiert die SQL-Standard Transaction Isolation Levels, die definieren, welche Anomalien zwischen gleichzeitigen Transaktionen verhindert werden. Das Verständnis von Isolation Levels ist kritisch für jede Anwendung, die mit Datenbanken arbeitet, besonders wenn mehrere Benutzer gleichzeitig auf Daten zugreifen. Die Wahl des falschen Isolation Levels kann zu Race Conditions, inkonsistenten Daten, oder Performance-Problemen führen.
Transaction Isolation ist Teil des ACID-Prinzips (Atomicity, Consistency, Isolation, Durability). Isolation stellt sicher, dass gleichzeitige Transaktionen sich nicht gegenseitig beeinflussen, während sie gleichzeitig ausgeführt werden. Aber vollständige Isolation würde Performance erheblich beeinträchtigen - daher definiert der SQL-Standard verschiedene Isolation Levels, die verschiedene Grade von Isolation bieten.
PostgreSQL implementiert drei der vier SQL-Standard Isolation Levels: Read Committed (Standard), Repeatable Read, und Serializable. Read Uncommitted wird nicht unterstützt, weil PostgreSQL es nicht benötigt - Read Committed verhindert bereits Dirty Reads. Jedes Level verhindert bestimmte Anomalien, aber erlaubt andere, um Performance zu optimieren.
Die SQL-Standard Isolation Levels
Der SQL-Standard definiert vier Isolation Levels, basierend auf drei Anomalien, die verhindert werden können:
Read Uncommitted: Verhindert keine Anomalien. Erlaubt Dirty Reads, Non-Repeatable Reads, und Phantom Reads. Dies ist das niedrigste Level und wird von PostgreSQL nicht unterstützt.
Read Committed: Verhindert Dirty Reads. Erlaubt Non-Repeatable Reads und Phantom Reads. Dies ist das Standard-Level in PostgreSQL.
Repeatable Read: Verhindert Dirty Reads und Non-Repeatable Reads. Erlaubt Phantom Reads. PostgreSQL implementiert dieses Level mit Snapshot Isolation.
Serializable: Verhindert alle drei Anomalien. Dies ist das höchste Level und bietet vollständige Isolation, aber kann Performance beeinträchtigen.
Die drei Anomalien
Dirty Read: Ein Dirty Read tritt auf, wenn eine Transaktion Daten liest, die von einer anderen Transaktion geändert wurden, aber noch nicht committed wurden. Wenn die andere Transaktion rollback wird, hat die erste Transaktion inkonsistente Daten gelesen. Read Committed und höhere Levels verhindern Dirty Reads.
Non-Repeatable Read: Ein Non-Repeatable Read tritt auf, wenn eine Transaktion dieselbe Zeile zweimal liest und unterschiedliche Werte erhält, weil eine andere Transaktion die Zeile zwischen den beiden Reads geändert hat. Repeatable Read und höhere Levels verhindern Non-Repeatable Reads.
Phantom Read: Ein Phantom Read tritt auf, wenn eine Transaktion eine Query zweimal ausführt und unterschiedliche Zeilen erhält, weil eine andere Transaktion neue Zeilen eingefügt hat, die die Query-Bedingung erfüllen. Nur Serializable verhindert Phantom Reads.
Read Committed (Standard)
Read Committed ist das Standard-Isolation Level in PostgreSQL. Jede Query in einer Transaktion sieht nur Daten, die zum Zeitpunkt des Query-Starts bereits committed waren. Dies bedeutet, dass verschiedene Queries in derselben Transaktion unterschiedliche Snapshots sehen können, wenn andere Transaktionen zwischen den Queries committen.
Wie es funktioniert: PostgreSQL verwendet Multi-Version Concurrency Control (MVCC). Jede Transaktion sieht eine Snapshot der Datenbank zum Zeitpunkt des Query-Starts. Wenn eine andere Transaktion Daten ändert und committet, während Ihre Transaktion läuft, sehen Sie diese Änderungen erst in der nächsten Query.
Vorteile: Read Committed ist performant und verhindert Dirty Reads. Es ist für die meisten Anwendungen ausreichend.
Nachteile: Non-Repeatable Reads und Phantom Reads sind möglich. Wenn Ihre Anwendung diese Anomalien nicht tolerieren kann, benötigen Sie ein höheres Isolation Level.
Beispiel:
sql
-- Transaktion 1
BEGIN;
SELECT balance FROM accounts WHERE id = 1; -- Liest 1000
-- Transaktion 2 ändert balance auf 1500 und committet
SELECT balance FROM accounts WHERE id = 1; -- Liest 1500 (Non-Repeatable Read)
COMMIT;
Repeatable Read
Repeatable Read verhindert Non-Repeatable Reads, indem alle Queries in einer Transaktion denselben Snapshot sehen. Der Snapshot wird beim ersten Query in der Transaktion erstellt und bleibt für die gesamte Transaktion konsistent.
Wie es funktioniert: PostgreSQL erstellt einen Snapshot beim ersten Query in der Transaktion. Alle nachfolgenden Queries in derselben Transaktion sehen denselben Snapshot, unabhängig davon, was andere Transaktionen ändern.
Vorteile: Verhindert Non-Repeatable Reads. Nützlich für Anwendungen, die konsistente Reads über die gesamte Transaktion benötigen.
Nachteile: Phantom Reads sind immer noch möglich. Kann zu Serialization Errors führen, wenn zwei Transaktionen versuchen, dieselben Zeilen zu ändern.
Serialization Errors: Wenn zwei Transaktionen im Repeatable Read Mode versuchen, Zeilen zu ändern, die die andere Transaktion bereits geändert hat, gibt PostgreSQL einen Serialization Error. Die Anwendung muss die Transaktion wiederholen.
Beispiel:
sql
-- Transaktion 1
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT balance FROM accounts WHERE id = 1; -- Liest 1000
-- Transaktion 2 ändert balance auf 1500 und committet
SELECT balance FROM accounts WHERE id = 1; -- Liest immer noch 1000 (konsistent)
COMMIT;
Serializable
Serializable ist das höchste Isolation Level und verhindert alle drei Anomalien. Es stellt sicher, dass das Ergebnis gleichzeitiger Transaktionen dasselbe ist, als ob sie sequenziell ausgeführt worden wären.
Wie es funktioniert: PostgreSQL verwendet Serializable Snapshot Isolation (SSI), eine optimistische Concurrency Control Methode. Anstatt Locks zu verwenden, überwacht PostgreSQL Abhängigkeiten zwischen Transaktionen und erkennt, wenn eine Serialisierung verletzt würde.
Vorteile: Vollständige Isolation. Verhindert alle Anomalien. Garantiert konsistente Daten.
Nachteile: Kann zu Serialization Errors führen, die Transaktionen wiederholen müssen. Kann Performance beeinträchtigen, besonders bei hoher Concurrency.
Serialization Errors: Serialization Errors treten auf, wenn PostgreSQL erkennt, dass das Ergebnis nicht serialisierbar wäre. Die Anwendung muss die Transaktion wiederholen. Dies ist normal und erwartet bei Serializable.
Beispiel:
sql
-- Transaktion 1
BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT * FROM accounts WHERE balance > 1000; -- Liest 2 Zeilen
-- Transaktion 2 fügt neue Zeile mit balance > 1000 ein und committet
SELECT * FROM accounts WHERE balance > 1000; -- Liest immer noch 2 Zeilen (kein Phantom Read)
COMMIT;
Praktische Überlegungen
Wann Read Committed verwenden: Read Committed ist für die meisten Anwendungen ausreichend. Es ist performant und verhindert die häufigsten Probleme (Dirty Reads). Verwenden Sie Read Committed, wenn Ihre Anwendung Non-Repeatable Reads oder Phantom Reads tolerieren kann.
Wann Repeatable Read verwenden: Verwenden Sie Repeatable Read, wenn Ihre Anwendung konsistente Reads über die gesamte Transaktion benötigt. Beispiel: Eine Transaktion, die mehrere Queries ausführt und erwartet, dass die Daten zwischen den Queries konsistent bleiben.
Wann Serializable verwenden: Verwenden Sie Serializable nur, wenn Sie vollständige Isolation benötigen und Serialization Errors handhaben können. Serializable ist für kritische Operationen, wo Datenkonsistenz wichtiger ist als Performance.
Performance-Impact: Höhere Isolation Levels können Performance beeinträchtigen, besonders bei hoher Concurrency. Read Committed ist am performantesten, Serializable am langsamsten. Aber der Impact hängt von der Workload ab - für viele Anwendungen ist der Unterschied minimal.
Serialization Error Handling: Wenn Sie Repeatable Read oder Serializable verwenden, müssen Sie Serialization Errors handhaben. Die Anwendung sollte Transaktionen wiederholen, wenn ein Serialization Error auftritt. Dies ist normal und erwartet.
Häufige Fehler
Falsches Isolation Level: Verwenden Sie nicht ein höheres Isolation Level als nötig. Höhere Levels können Performance beeinträchtigen und Serialization Errors verursachen.
Ignorieren von Serialization Errors: Serialization Errors müssen behandelt werden. Die Anwendung sollte Transaktionen wiederholen, wenn ein Error auftritt.
Annahme, dass höhere Levels immer besser sind: Höhere Isolation Levels sind nicht immer besser. Sie können Performance beeinträchtigen und sind oft nicht nötig.
Vergessen, dass Phantom Reads möglich sind: In Read Committed und Repeatable Read sind Phantom Reads möglich. Wenn Ihre Anwendung Phantom Reads nicht tolerieren kann, benötigen Sie Serializable.
Best Practices
Beginnen Sie mit Read Committed: Read Committed ist für die meisten Anwendungen ausreichend. Erhöhen Sie das Level nur, wenn Sie spezifische Probleme haben.
Testen Sie Isolation Levels: Testen Sie verschiedene Isolation Levels in Ihrer Anwendung, um zu sehen, welches am besten funktioniert.
Handhaben Sie Serialization Errors: Wenn Sie Repeatable Read oder Serializable verwenden, implementieren Sie Retry-Logik für Serialization Errors.
Überwachen Sie Performance: Überwachen Sie Performance bei verschiedenen Isolation Levels. Höhere Levels können Performance beeinträchtigen.
Dokumentieren Sie Ihre Wahl: Dokumentieren Sie, warum Sie ein bestimmtes Isolation Level gewählt haben. Dies hilft anderen Entwicklern, Ihre Entscheidung zu verstehen.
Kommentare