Wie Datenbanken gleichzeitige Transaktionen verwalten, ohne Daten zu beschädigen
Die Nebenläufigkeitskontrolle ist die Menge von Mechanismen, die ein Datenbankmanagementsystem (DBMS) verwendet, um gleichzeitige Transaktionen zu verwalten, ohne Daten zu beschädigen. Wenn mehrere Benutzer oder Prozesse gleichzeitig lesen und schreiben, führt unkontrollierter Zugriff zu Datenanomalien wie verlorenen Updates, schmutzigen Lesevorgängen und inkonsistenten Ergebnissen, die sich kaskadenartig durch nachgelagerte Systeme ziehen können.
Die Nebenläufigkeitskontrolle erzwingt das „I“ (Isolation) in ACID: gleichzeitige Transaktionen sollten das gleiche Ergebnis liefern, als ob sie nacheinander ausgeführt würden, eine Eigenschaft, die als Serialisierbarkeit bekannt ist. Ohne sie könnte eine Bankanwendung Überweisungen verlieren, ein Inventarsystem zu viele Produkte verkaufen und eine Analyse-Pipeline Berichte auf Basis von halb geschriebenen Daten erstellen.
Dieser Leitfaden behandelt die Kernprobleme der Nebenläufigkeit, die wichtigsten Mechanismen zur Lösung – Sperren, Multi-Version Concurrency Control (MVCC) und optimistische und pessimistische Strategien – sowie Isolationsstufen, Deadlock-Behandlung und praktische Anleitungen zur Auswahl des richtigen Ansatzes für Ihre Workload.
Moderne Datenbanksysteme verarbeiten routinemäßig Tausende bis Millionen gleichzeitiger Transaktionen pro Sekunde. Jedes Mal, wenn ein Benutzer eine Abfrage einreicht, einen Datensatz aktualisiert oder eine ETL-Pipeline startet, muss das DBMS diese Operation neben jeder anderen gleichzeitig stattfindenden Operation koordinieren. Ohne Nebenläufigkeitskontrolle führen verschachtelte Transaktionen zu unvorhersehbaren, falschen Ergebnissen.
Betrachten Sie ein konkretes Szenario: Zwei Benutzer aktualisieren gleichzeitig denselben Bankkontostand. Benutzer A liest den Saldo (1.000 €), zieht 200 € ab und schreibt 800 €. Benutzer B liest ebenfalls 1.000 €, fügt 500 € hinzu und schreibt 1.500 €. Das korrekte Ergebnis hängt von der Reihenfolge ab – entweder 800 € oder 1.500 € –, aber ohne Koordination überschreibt ein Schreibvorgang einfach den anderen. Dies ist ein klassisches verlorenes Update, eine von mehreren Anomalien, die die Nebenläufigkeitskontrolle verhindern soll.
Die Nebenläufigkeitskontrolle erzwingt die Serialisierbarkeit: Das Ergebnis der gleichzeitigen Ausführung muss einer seriellen Reihenfolge derselben Transaktionen entsprechen. Dies unterstützt direkt alle vier ACID-Garantien – Atomarität (Alles oder Nichts), Konsistenz (gültige Zustandsübergänge), Isolation (keine Interferenzen) und Dauerhaftigkeit (bestätigte Änderungen sind permanent). Dies garantiert die Grundlage, auf der jede Anwendung Vertrauen in ihre Daten aufbaut.
Bevor wir Lösungen untersuchen, ist es hilfreich, die spezifischen Anomalien zu verstehen, die auftreten, wenn gleichzeitige Transaktionen ohne ordnungsgemäße Kontrollen ausgeführt werden.
Diese vier Anomalien sind der „Grund“, warum es jeden Mechanismus zur Nebenläufigkeitskontrolle gibt. Jeder Mechanismus verhindert einige oder alle davon, abhängig von der von ihm erzwungenen Isolationsebene.
Sperren ist der älteste und intuitivste Ansatz zur Nebenläufigkeitskontrolle. Das DBMS weist Datenelementen – Zeilen, Seiten oder ganzen Tabellen – Sperren zu, bevor Transaktionen darauf zugreifen dürfen. Eine Sperre fungiert als Gatekeeper: Wenn eine andere Transaktion bereits eine widersprüchliche Sperre hält, muss die anfragende Transaktion warten.
Die Granularität der Sperren führt zu einem wichtigen Kompromiss. Zeilen-Sperren maximieren die Nebenläufigkeit, da nicht verwandte Zeilen zugänglich bleiben, aber sie verursachen zusätzlichen Aufwand, da das System viele einzelne Sperren verfolgen muss. Tabellen-Sperren sind einfacher und kostengünstiger zu verwalten, zwingen aber nicht verwandte Transaktionen zum Warten, was den Durchsatz reduziert.
Two-Phase Locking ist das Standardprotokoll zur Gewährleistung der Serialisierbarkeit durch Sperren. Es teilt jede Transaktion in zwei Phasen. Während der Wachstumsphase erwirbt eine Transaktion alle benötigten Sperren, gibt aber niemals welche frei. Sobald sie ihre erste Sperre freigibt, tritt sie in die Schrumpfphase ein und kann nur Sperren freigeben, niemals neue erwerben. Diese Regel stellt sicher, dass die Reihenfolge, in der Transaktionen Datenelemente sperren, konsistent ist, was wiederum einen serialisierbaren Ablauf garantiert.
Strict 2PL ist eine gängige Variante, die alle Sperren hält, bis die Transaktion bestätigt wird. Dies verhindert kaskadierende Rollbacks, eine Situation, in der eine abgebrochene Transaktion andere Transaktionen, die ihre nicht bestätigten Daten gelesen haben, zum Abbruch zwingt. Die meisten relationalen Datenbanken, die sperrbasierte Nebenläufigkeitskontrolle verwenden, implementieren eine Form von Strict 2PL.
Der Nachteil von 2PL ist, dass es zu Blockierungen führt – Transaktionen müssen auf Sperren warten, die von anderen gehalten werden – und die Bedingungen für Deadlocks schafft.
MVCC verfolgt einen grundlegend anderen Ansatz: Anstatt den Zugriff zu blockieren, verwaltet es mehrere Versionen jedes Datenelements, sodass jede Transaktion einen konsistenten Schnappschuss der Datenbank sieht. Wenn eine Transaktion eine Zeile schreibt, erstellt MVCC eine neue Version, anstatt die vorhandene zu überschreiben. Leser sehen weiterhin die Version, die zum Zeitpunkt des Starts ihrer Transaktion aktuell war.
Der Hauptvorteil ist, dass Leser niemals Schreiber blockieren und Schreiber niemals Leser blockieren. Dies ist ein erheblicher Leistungsvorteil für Lese-intensive Workloads, weshalb MVCC der dominierende Mechanismus zur Nebenläufigkeitskontrolle in modernen Datenbanken ist. PostgreSQL, MySQL/InnoDB, Oracle und die meisten zeitgenössischen OLTP- und OLAP-Systeme verwenden eine Form von MVCC.
Schreibkonflikte werden zum Zeitpunkt der Bestätigung erkannt. Wenn zwei Transaktionen dieselbe Zeile ändern, gewinnt die erste, die bestätigt wird, und die zweite muss es erneut versuchen. Dieser optimistische Ansatz zur Auflösung von Schreibkonflikten hält den Durchsatz hoch, wenn Konflikte selten sind, was meistens der Fall ist.
Snapshot Isolation ist die gängigste MVCC-basierte Isolationsebene. Jede Transaktion sieht die Datenbank so, wie sie zum Zeitpunkt des Starts der Transaktion war, was schmutzige Lesevorgänge und nicht wiederholbare Lesevorgänge verhindert. Snapshot Isolation erlaubt jedoch eine subtile Anomalie namens Write Skew, bei der zwei Transaktionen überlappende Daten lesen und dann Schreibvorgänge basierend auf dem Gelesenen durchführen, was zu einem Zustand führt, den keine der beiden Transaktionen allein erzeugt hätte. Serializable Snapshot Isolation (SSI) behebt diese Lücke auf Kosten zusätzlichen Aufwands.
MVCC bringt eigene Kompromisse mit sich. Die Verwaltung mehrerer Versionen verbraucht zusätzlichen Speicherplatz, und das System muss regelmäßig veraltete Versionen bereinigen – ein Prozess, der in PostgreSQL als VACUUM bezeichnet wird. Bei starker Schreibkonfliktlast können die Kosten für Wiederholungsversuche und der Aufwand für die Versionsverwaltung erheblich werden.
Über die spezifischen Mechanismen von Sperren und MVCC hinaus fallen Strategien zur Nebenläufigkeitskontrolle in zwei philosophische Lager: pessimistisch und optimistisch. Zu verstehen, welches Lager zu Ihrer Workload passt, ist eine der folgenreichsten architektonischen Entscheidungen, die ein Datenteam treffen kann.
Die pessimistische Nebenläufigkeitskontrolle geht davon aus, dass Konflikte wahrscheinlich sind und verhindert sie, indem sie Sperren erwirbt, bevor auf Daten zugegriffen wird. Dieser Ansatz eignet sich am besten für schreibintensive Workloads, bei denen Konflikte häufig auftreten und die Kosten für das Zurückrollen und Wiederholen einer Transaktion hoch sind. Finanzbuchhaltungssysteme, bei denen jeder Schreibvorgang sequenziert und verifiziert werden muss, sind ein klassischer Anwendungsfall.
Der Nachteil ist die Blockierung. Bei hoher Nebenläufigkeit reihen sich Transaktionen in Warteschlangen ein und warten auf Sperren, was den Durchsatz reduziert. Im schlimmsten Fall führen konkurrierende Sperranforderungen zu Deadlocks.
Die optimistische Nebenläufigkeitskontrolle geht davon aus, dass Konflikte selten sind. Transaktionen werden frei ausgeführt, ohne Sperren zu erwerben, und arbeiten mit ihren eigenen privaten Kopien der Daten. Zum Zeitpunkt der Bestätigung prüft das System, ob die Lese- und Schreibvorgänge der Transaktion mit anderen bestätigten Transaktionen kollidieren. Wenn keine Konflikte gefunden werden, wird die Transaktion bestätigt. Wenn ein Konflikt erkannt wird, wird die Transaktion zurückgerollt und wiederholt.
Dieses dreistufige Modell — lesen, validieren, schreiben — eignet sich hervorragend für Umgebungen, in denen die meisten Transaktionen nicht auf dieselben Daten zugreifen. Content-Management-Systeme, Katalog-Browsing-Anwendungen und Reporting-Dashboards sind typische Beispiele.
Der Nachteil sind verschwendete Arbeit. Wenn Konflikte häufig auftreten, führen Transaktionen wiederholt ihre vollständige Logik aus, nur um bei der Validierung zurückgerollt zu werden, wodurch Ressourcen verbraucht werden, ohne Ergebnisse zu erzielen.
Die Wahl zwischen pessimistischer und optimistischer Steuerung hängt von den Merkmalen der Workload ab. Hohe Schreibkonflikte, kurze Transaktionen und eine geringe Toleranz für Wiederholungsversuche deuten auf eine pessimistische Steuerung hin. Leseintensive Workloads mit geringer Konfliktwahrscheinlichkeit und hohen Nebenläufigkeitsanforderungen begünstigen optimistische oder MVCC-basierte Ansätze.
In der Praxis mischen viele Produktionssysteme beide Strategien. Ein gängiges Muster verwendet MVCC für allgemeine Lese- und Schreibvorgänge, wendet jedoch pessimistische Sperren auf bekannte Hot-Spot-Zeilen an — zum Beispiel unter Verwendung von SELECT ... FOR UPDATE, um eine bestimmte Zeile zu sperren, um die mehrere Transaktionen gleichzeitig konkurrieren.