14 KiB
Executable File
#datenbank ➡️#Zusammenfassung
MVCC („==Multi-Version Concurrency Control==“) ist ein Verfahren, mit dem Datenbanken gleichzeitige Zugriffe (Concurrency) von vielen Benutzern/Programmen steuern, ohne dass sie sich ständig gegenseitig blockieren.
Ich gehe Schritt für Schritt vor:
- Ausgangspunkt: Was ist das Problem überhaupt?
- Grunddefinition von MVCC (für Einsteiger)
- Wie funktioniert das intern grob?
- Abgrenzung zu verwandten Konzepten
- Welche Probleme löst MVCC konkret?
- Welche Herausforderungen/Nachteile bringt MVCC mit sich?
- Praxisnahe Beispiele
1. Ausgangspunkt: Wozu brauche ich so etwas?
In einer typischen Anwendung greifen viele Nutzer gleichzeitig auf dieselbe Datenbank zu:
- Nutzer A liest Daten (z.B. Produktliste im Shop)
- Nutzer B aktualisiert Daten (z.B. ändert Preise)
- Nutzer C führt einen großen Report aus (z.B. tägliche Umsatzauswertung)
Ziel:
Alle sollen gleichzeitig arbeiten können, ohne Fehler wie:
- „Dirty Reads“ (jemand liest halbfertige Änderungen)
- Inkonsistente Sicht auf die Daten
- Datenverlust, weil gleichzeitige Änderungen sich überschreiben
- Dauerhafte Sperren und Deadlocks, die das System verlangsamen oder blockieren
Dazu gibt es das Konzept der Transaktionen (ACID):
- Atomicity: Alles oder nichts.
- Consistency: Regeln der Datenbank bleiben erfüllt.
- Isolation: Transaktionen beeinflussen sich möglichst wenig.
- Durability: Einmal bestätigte Daten gehen nicht verloren.
Die Isolation ist das Kernfeld, in dem MVCC ins Spiel kommt:
Wie sorgt man dafür, dass viele Transaktionen gleichzeitig laufen können, ohne sich ständig zu blockieren?
2. Grunddefinition von MVCC (für Einsteiger)
MVCC (Multi-Version Concurrency Control) bedeutet:
- Jede Zeile (Row) in einer Tabelle existiert in mehreren Versionen.
- Jede Transaktion sieht die Daten „so, wie sie zu einem bestimmten Zeitpunkt waren“ – sie arbeitet mit einem Snapshot (Momentaufnahme).
- Wenn jemand etwas ändert, wird nicht die existierende Zeile überschrieben, sondern eine neue Version der Zeile angelegt.
- Für jede Transaktion gibt es Regeln, welche Version einer Zeile sie sehen darf.
Anschaulich:
- Stell dir eine Tabelle als Tabelle in Excel vor.
- MVCC macht von einigen Zeilen „Kopien“, wenn sie geändert werden.
- Du (deine Transaktion) siehst eine konsistente Version des Tabellenblatts zu dem Zeitpunkt, als du angefangen hast – auch wenn andere währenddessen schon neue Versionen erzeugt haben.
3. Wie funktioniert MVCC grob intern?
Vereinfachtes Modell (z.B. ähnlich wie in PostgreSQL/InnoDB):
3.1. Versionen von Zeilen
Statt einer Zeile gibt es:
- Version 1: erstellt von Transaktion T1
- Version 2: später erstellt von Transaktion T2 (z.B. nach einem UPDATE)
- Version 3: später erstellt von T3, usw.
Jede Version hat Metadaten, z.B.:
- „ab Transaktion X gültig“
- „bis Transaktion Y gültig“
(oder „gelöscht ab Transaktion Y“)
3.2. Was sieht eine Transaktion?
Wenn eine neue Transaktion T liest:
-
Sie merkt sich einen „Zeitpunkt“ (z.B. eine Transaktions-ID oder Timestamp).
-
Beim Lesen prüft sie pro Zeile:
- Wurde diese Version vor meinem Start erzeugt und war zu dem Zeitpunkt schon committed?
- Wurde sie danach erzeugt? Dann darf ich sie nicht sehen.
- Ist sie bereits gelöscht oder von einer laufenden Transaktion verändert, die noch nicht committed ist? Dann gilt sie für mich ggf. als nicht existent.
Das Ergebnis:
Du bekommst einen konsistenten Snapshot der Daten – so, als ob alle Änderungen nach deinem Start noch nicht existieren.
3.3. Schreiben mit MVCC
Beim UPDATE einer Zeile:
- Alte Version bleibt im Speicher (zunächst noch sichtbar für alte Transaktionen).
- Neue Version wird erzeugt, mit „gültig ab Transaktion T“.
- Für neu startende Transaktionen ist nach Commit nur noch die neue Version relevant.
Beim DELETE:
- Die Zeile bekommt sozusagen ein „Todes-Zeitstempel“ (gültig bis T).
- Alte Transaktionen sehen sie noch, neue nicht mehr.
3.4. Aufräumen („Vacuum“, Garbage Collection)
Alte Versionen, die keine Transaktion mehr sehen kann, sind Müll:
- Es gibt Prozess(e), die diese alten Versionen von Zeit zu Zeit physisch entfernen.
- Beispiel-Begriff: in PostgreSQL „VACUUM“.
4. Abgrenzung zu verwandten Konzepten
4.1. MVCC vs. „klassische“ Sperren (Locking)
Früher (oder in einfachen Systemen):
- Jede Transaktion, die liest oder schreibt, setzt Sperren (Locks) auf Zeilen oder ganze Tabellen.
- Wenn du eine Zeile liest, kann ein strenges System diese Zeile sperren, sodass andere sie nicht ändern dürfen, bis du fertig bist.
- Das kann zu Blockaden und Deadlocks führen.
MVCC-Ansatz:
- Leser blockieren Schreiber nicht (oder nur minimal).
- Schreiber blockieren Leser nicht (solange es nur um das Lesen bereits existierender, „alter“ Versionen geht).
- Es gibt zwar weiterhin Sperren, aber sie sind oft nur für Konfliktfälle relevant (z.B. zwei Transaktionen, die dieselbe Zeile gleichzeitig ändern wollen).
MVCC ersetzt also nicht jede Form von Locking, sondern reduziert deren Notwendigkeit für Lesezugriffe stark.
4.2. MVCC vs. Snapshot Isolation
- MVCC ist ein Mechanismus: mehrere Versionen von Zeilen, Snapshots, Sichtbarkeitsregeln.
- Snapshot Isolation ist eine Isolationsebene (ein bestimmter Garantiesatz für Transaktionen), die auf MVCC aufbaut.
Viele Systeme verwenden MVCC, um Snapshot Isolation umzusetzen:
- Jede Transaktion sieht den Datenbestand so, wie er beim Start (oder beim Start des Statements) war.
- Aber: Snapshot Isolation ist oft nicht vollständig serialisierbar (siehe Write Skew Problem weiter unten).
Kurz:
MVCC = Wie die Datenbank Versionen verwaltet.
Snapshot Isolation = Welche Sicht und Garantien Transaktionen bekommen.
4.3. MVCC vs. Optimistic Concurrency Control (OCC)
- Optimistic Concurrency Control geht davon aus: Konflikte sind selten, man lässt Transaktionen frei operieren und prüft erst beim Commit, ob es Konflikte gab.
- Viele MVCC-Systeme verwenden Elemente von OCC: Sie lassen Transaktionen auf einem Snapshot arbeiten und prüfen erst beim Commit, ob sie z.B. eine Zeile verändert haben, die inzwischen jemand anders schon geändert hat.
MVCC ist also kein direkter Gegensatz zu OCC, sondern eher eine Technik, mit der man OCC effizient implementieren kann.
5. Welche Probleme werden durch MVCC gelöst?
5.1. Leser blockieren Schreiber nicht (und umgekehrt)
Problem ohne MVCC:
Großer Report läuft lange und sperrt viele Zeilen oder Tabellen. Schreibende Transaktionen können nicht mehr weiterarbeiten.
Mit MVCC:
- Der Report bekommt einen Snapshot.
- Während der Report läuft:
- Andere Transaktionen können neue Versionen schreiben.
- Der Report liest weiterhin die alten Versionen, die für ihn gültig sind.
- Ergebnis: lange lesende Transaktionen blockieren das System weit weniger.
Praxisbeispiel:
- Ein Online-Shop:
- Ein Reporting-Job zählt alle Bestellungen des letzten Monats.
- Gleichzeitig legen Kunden neue Bestellungen an.
- Mit MVCC:
- Reporting-Job sieht einen stabilen Stand („Stand 10:00 Uhr“).
- Kunden können weiter bestellen; diese Bestellungen erscheinen evtl. nicht im aktuellen Report, aber im nächsten.
5.2. Konsistente Lese-Sichten
Lesende Transaktionen sehen eine konsistente Momentaufnahme, nicht einen Mix aus „halb alt, halb neu“.
Beispiel:
- Du willst sicherstellen, dass Summe aller Kontosalden = 0 (Bilanz).
- Ohne MVCC könntest du beim Lesen ein Konto schon nach der neuen Buchung sehen, ein anderes aber noch vor der Buchung – Ergebnis: inkonsistente Summe.
- Mit MVCC liest du alles so, wie es zu deinem Startzeitpunkt war.
5.3. Hohe Skalierbarkeit für viele Leser
MVCC ist besonders stark in Systemen mit:
- hohem Leseanteil (Reports, Dashboards, APIs)
- vielen parallelen Nutzern
Weil Leser nicht permanent Schreiblocks blockieren, kann das System mehr parallele Anfragen verarbeiten.
5.4. Reduzierte Deadlocks
Deadlocks entstehen oft, wenn zwei Transaktionen sich gegenseitig Sperren wegnehmen wollen.
Da MVCC Leser weitgehend von Locks entkoppelt, reduziert sich die Deadlock-Wahrscheinlichkeit, insbesondere bei reinen Lesezugriffen.
6. Welche Herausforderungen und Nachteile hat MVCC?
6.1. Speicher-Overhead durch viele Versionen
Da alte Versionen zunächst liegen bleiben, kann es passieren:
- Tabellen werden „aufgebläht“ (Bloat).
- Disk-Verbrauch steigt, Indexe werden größer, Performance sinkt.
Darum braucht man:
- Mechanismen zur Bereinigung (VACUUM, Garbage Collection)
- Tuning (z.B. wie aggressiv wird gereinigt?)
Praxisproblem:
Ein System mit dauernd vielen Updates/Deletes, aber schlecht eingestelltem Vacuum, kann plötzlich deutlich langsamer werden, weil die Tabellen intern riesig sind.
6.2. Lang laufende Transaktionen halten alte Versionen fest
Wenn eine Transaktion sehr lange offen bleibt (z.B. ein Report, der 2 Stunden läuft):
- Alle Versionen, die für diese Transaktion sichtbar sein könnten, müssen erhalten bleiben.
- Die Datenbank kann diese Versionen nicht löschen, weil sie noch gebraucht werden.
- Folge: Bloat, mehr Speicherverbrauch, mehr I/O, langsamere Zugriffe.
Praxisnahe Konsequenz:
- Man versucht in produktiven Systemen:
- Lange Transaktionen zu vermeiden oder
- Sie auf Replikas/Read-Only-Kopien auszulagern.
6.3. Anomalien unter Snapshot Isolation (Write Skew)
Snapshot Isolation (häufig via MVCC umgesetzt) ist oft nicht vollständig serialisierbar.
Beispiel (vereinfacht):
- Zwei Ärzte müssen gleichzeitig im Krankenhaus Dienst haben.
- Regel: Es dürfen nie beide Ärzte gleichzeitig „Off-Duty“ sein.
- Tabelle
dienstplanmit Einträgen pro Arzt.
Transaktion T1:
BEGIN;
SELECT COUNT(*) FROM dienstplan WHERE on_duty = TRUE;
-- Ergebnis: 2 (Arzt A und B sind im Dienst)
-- T1 setzt Arzt A auf off-duty
UPDATE dienstplan SET on_duty = FALSE WHERE arzt = 'A';
COMMIT;
Transaktion T2 (parallel, mit eigenem Snapshot):
BEGIN;
SELECT COUNT(*) FROM dienstplan WHERE on_duty = TRUE;
-- Ergebnis: 2 (sieht noch alten Stand, beide on duty)
-- T2 setzt Arzt B auf off-duty
UPDATE dienstplan SET on_duty = FALSE WHERE arzt = 'B';
COMMIT;
Beide Transaktionen sehen denselben alten Stand („2 Ärzte im Dienst“).
Beide halten es für zulässig, je einen Arzt auf off-duty zu setzen.
Ergebnis nach beiden Commits: 0 Ärzte im Dienst, Regel verletzt.
Das ist ein typischer Write Skew – möglich unter Snapshot Isolation, obwohl MVCC sauber arbeitet.
Lösung:
- Strengere Isolationsebene: SERIALIZABLE (die DB verhindert durch zusätzliche Checks solche Fälle),
oder - Explizite Sperren / zusätzliche Constraints durch den Entwickler.
6.4. Komplexere Implementierung und Tuning
MVCC ist intern relativ komplex:
- Verwaltung von Versionen
- Sichtbarkeitslogik
- Garbage Collection
- Umgang mit Hot-Spot-Tabellen (sehr häufig aktualisierte Zeilen)
Für Administratoren/Entwickler bedeutet das:
- Man muss verstehen, wie die eigene DB MVCC implementiert (PostgreSQL, MySQL InnoDB, Oracle, SQL Server etc. machen es leicht unterschiedlich).
- Man muss Parameter fürs Aufräumen und für Isolationsebenen sinnvoll setzen.
7. Praxisnahe Beispiele
Beispiel 1: Webshop – Produktpreise ändern
Situation:
- Tabelle
produktemit Spaltepreis. - Viele Nutzer sehen gleichzeitig das Produkt im Shop.
- Ein Admin ändert den Preis.
Ohne MVCC (vereinfachte, pessimistische Sperrung):
- Admin setzt Lock auf Zeile/Produkt.
- Solange Admin noch nicht committed hat:
- Kunden könnten blockiert werden oder
- sie sehen unklare Zwischenzustände.
Mit MVCC:
- Admin-Transaktion erzeugt eine neue Version der Produktzeile mit neuem Preis.
- Kunden, die vor dem Commit lesen:
- sehen die alte Version (alten Preis).
- Kunden, die nach dem Commit lesen:
- sehen die neue Version (neuen Preis).
- Es gibt keine Lese-Blockade während der Preisänderung.
Beispiel 2: Reporting vs. Online-Transaktionen
- Ein Finance-Report soll Stand Tagesende alle Buchungen auswerten.
- Parallel buchen die Nutzer weiter.
Mit MVCC:
- Report bekommt einen Snapshot (z.B. Stand 23:59).
- Er arbeitet vielleicht 30 Minuten oder länger.
- Währenddessen können Buchungen von 00:00–00:30 weiter eingehen.
- Report sieht einen stabilen Zustand; neue Buchungen tauchen erst im nächsten Report auf.
Das ist ideal für:
- Data Warehouse Light
- tägliche Berichte
- Audits (reproduzierbare Sicht)
Beispiel 3: Problemfall lange Transaktion
- Ein Entwickler startet in der Entwicklungsumgebung eine Transaktion:
BEGIN;
SELECT * FROM grosse_tabelle; -- dauert lange
-- Entwickler vergisst COMMIT oder ROLLBACK
- Diese Transaktion bleibt offen.
- Alle Versionen, die seit Beginn dieser Transaktion geändert wurden, müssen aufgehoben werden.
- Vacuum/Garbage Collector kann viele alte Zeilen nicht löschen.
- Nach Stunden/Tagen:
- Tabellen aufgebläht,
- Performance sinkt,
- Admin muss offene Sessions identifizieren und beenden.
Lernpunkt:
Mit MVCC muss man auf offene, vergessene Transaktionen achten.
Zusammenfassung
- MVCC bedeutet: Zeilen werden nicht einfach überschrieben, sondern es gibt mehrere Versionen derselben Zeile.
- Leser und Schreiber können gleichzeitig arbeiten:
- Leser sehen einen konsistenten Snapshot,
- Schreiber erzeugen neue Versionen, ohne Leser direkt zu blockieren.
- MVCC ist der Mechanismus, auf dem Isolationsebenen wie Snapshot Isolation aufbauen.
- Vorteile:
- Hohe Parallelität,
- Weniger Sperrkonflikte,
- Konsistente Sichten für Reports.
- Nachteile/Herausforderungen:
- Mehr Speicherbedarf durch alte Versionen,
- Notwendigkeit von Aufräumprozessen,
- Vorsicht bei langen Transaktionen,
- bestimmte Anomalien (z.B. Write Skew) bei Snapshot Isolation.