Veröffentlicht: 21. August 2019
von Burak Yavuz, Michael Armbrust und Brenner Heintz

Das Transaktionsprotokoll ist der Schlüssel zum Verständnis von Delta Lake, da es der rote Faden ist, der sich durch viele seiner wichtigsten Funktionen zieht, darunter ACID-Transaktionen, skalierbare Metadatenverarbeitung, Zeitreise und mehr. In diesem Artikel werden wir untersuchen, was das Delta Lake-Transaktionsprotokoll ist, wie es auf Dateiebene funktioniert und wie es eine elegante Lösung für das Problem mehrerer gleichzeitiger Lese- und Schreibvorgänge bietet.
Das Delta Lake-Transaktionsprotokoll (auch bekannt als DeltaLog) ist eine geordnete Aufzeichnung jeder Transaktion, die jemals an einer Delta Lake-Tabelle seit ihrer Erstellung durchgeführt wurde.
Delta Lake basiert auf Apache Spark™, um mehreren Lesern und Schreibern einer bestimmten Tabelle zu ermöglichen, gleichzeitig an der Tabelle zu arbeiten. Um den Benutzern jederzeit korrekte Ansichten der Daten anzuzeigen, dient das Delta Lake-Transaktionsprotokoll als Single Source of Truth – das zentrale Repository, das alle Änderungen verfolgt, die Benutzer an der Tabelle vornehmen.
Wenn ein Benutzer zum ersten Mal eine Delta Lake-Tabelle liest oder eine neue Abfrage auf einer geöffneten Tabelle ausführt, die seit dem letzten Lesen geändert wurde, überprüft Spark das Transaktionsprotokoll, um festzustellen, welche neuen Transaktionen in der Tabelle veröffentlicht wurden, und aktualisiert dann die Tabelle des Endbenutzers mit diesen neuen Änderungen. Dies stellt sicher, dass die Version einer Tabelle eines Benutzers immer mit dem Masterdatensatz ab dem Zeitpunkt der letzten Abfrage synchronisiert ist und dass Benutzer keine abweichenden, widersprüchlichen Änderungen an einer Tabelle vornehmen können.
Eine der vier Eigenschaften von ACID-Transaktionen, die Atomizität, garantiert, dass Operationen (wie ein INSERT oder UPDATE), die auf Ihrem Data Lake ausgeführt werden, entweder vollständig abgeschlossen werden oder gar nicht. Ohne diese Eigenschaft kann ein Hardwarefehler oder ein Softwarefehler allzu leicht dazu führen, dass Daten nur teilweise in eine Tabelle geschrieben werden, was zu unordentlichen oder beschädigten Daten führt.
Das Transaktionsprotokoll ist der Mechanismus, durch den Delta Lake die Garantie der Atomizität bieten kann. Für alle praktischen Zwecke gilt: Wenn es nicht im Transaktionsprotokoll aufgezeichnet ist, ist es nie geschehen. Indem nur Transaktionen aufgezeichnet werden, die vollständig und vollständig ausgeführt werden, und diese Aufzeichnung als Single Source of Truth verwendet wird, ermöglicht das Delta Lake-Transaktionsprotokoll den Benutzern, über ihre Daten nachzudenken und sich auf Petabyte-Ebene auf deren grundlegende Vertrauenswürdigkeit zu verlassen.
Immer wenn ein Benutzer eine Operation zur Änderung einer Tabelle durchführt (z. B. ein INSERT, UPDATE oder DELETE), unterteilt Delta Lake diese Operation in eine Reihe diskreter Schritte, die aus einer oder mehreren der folgenden Aktionen bestehen.
Diese Aktionen werden dann im Transaktionsprotokoll als geordnete, atomare Einheiten aufgezeichnet, die als Commits bezeichnet werden.
Nehmen wir beispielsweise an, ein Benutzer erstellt eine Transaktion, um einer Tabelle eine neue Spalte hinzuzufügen und weitere Daten hinzuzufügen. Delta Lake würde diese Transaktion in ihre Bestandteile zerlegen und sie nach Abschluss der Transaktion als die folgenden Commits zum Transaktionsprotokoll hinzufügen:
Wenn ein Benutzer eine Delta Lake-Tabelle erstellt, wird das Transaktionsprotokoll dieser Tabelle automatisch im Unterverzeichnis _delta_log erstellt. Wenn er Änderungen an dieser Tabelle vornimmt, werden diese Änderungen als geordnete, atomare Commits im Transaktionsprotokoll aufgezeichnet. Jeder Commit wird als JSON-Datei ausgegeben, beginnend mit 000000.json. Zusätzliche Änderungen an der Tabelle generieren nachfolgende JSON-Dateien in aufsteigender numerischer Reihenfolge, sodass der nächste Commit als 000001.json, der folgende als 000002.json usw. ausgegeben wird.
Nehmen wir also beispielsweise an, wir fügen unserer Tabelle zusätzliche Datensätze aus den Datendateien 1.parquet und 2.parquet hinzu. Diese Transaktion würde automatisch dem Transaktionsprotokoll hinzugefügt und als Commit 000000.json auf der Festplatte gespeichert. Dann ändern wir vielleicht unsere Meinung und beschließen, diese Dateien zu entfernen und stattdessen eine neue Datei (3.parquet) hinzuzufügen. Diese Aktionen würden als nächster Commit im Transaktionsprotokoll als 000001.json aufgezeichnet, wie unten gezeigt.
Auch wenn 1.parquet und 2.parquet nicht mehr Teil unserer Delta Lake-Tabelle sind, werden ihre Hinzufügung und Entfernung dennoch im Transaktionsprotokoll aufgezeichnet, da diese Operationen an unserer Tabelle durchgeführt wurden – trotz der Tatsache, dass sie sich letztendlich gegenseitig aufgehoben haben. Delta Lake behält dennoch atomare Commits wie diese bei, um sicherzustellen, dass wir im Falle einer Überprüfung unserer Tabelle oder der Verwendung von „Zeitreisen“ sehen können, wie unsere Tabelle zu einem bestimmten Zeitpunkt aussah.
Außerdem entfernt Spark die Dateien nicht sofort von der Festplatte, auch wenn wir die zugrunde liegenden Datendateien aus unserer Tabelle entfernt haben. Benutzer können die nicht mehr benötigten Dateien mit VACUUM löschen.
Sobald wir mehrere Commits am Transaktionsprotokoll vorgenommen haben, speichert Delta Lake eine Checkpoint-Datei im Parquet-Format im selben Unterverzeichnis _delta_log. Delta Lake generiert automatisch Checkpoints nach Bedarf, um eine gute Leseleistung aufrechtzuerhalten.
Diese Checkpoint-Dateien speichern den gesamten Status der Tabelle zu einem bestimmten Zeitpunkt – im nativen Parquet-Format, das Spark schnell und einfach lesen kann. Mit anderen Worten, sie bieten dem Spark-Reader eine Art „Abkürzung“ zur vollständigen Reproduktion des Status einer Tabelle, die es Spark ermöglicht, die Neuverarbeitung von möglicherweise Tausenden von kleinen, ineffizienten JSON-Dateien zu vermeiden.
Um auf dem neuesten Stand zu sein, kann Spark eine listFrom-Operation ausführen, um alle Dateien im Transaktionsprotokoll anzuzeigen, schnell zur neuesten Checkpoint-Datei zu springen und nur die JSON-Commits zu verarbeiten, die seit dem Speichern der letzten Checkpoint-Datei vorgenommen wurden.
Um zu demonstrieren, wie dies funktioniert, stellen Sie sich vor, wir haben Commits bis einschließlich 000007.json erstellt, wie im Diagramm unten dargestellt. Spark ist durch diesen Commit auf dem neuesten Stand und hat automatisch die neueste Version der Tabelle im Speicher zwischengespeichert. In der Zwischenzeit haben jedoch mehrere andere Schreiber (vielleicht Ihre übereifrigen Teamkollegen) neue Daten in die Tabelle geschrieben und Commits bis einschließlich 0000012.json hinzugefügt.
Um diese neuen Transaktionen zu integrieren und den Status unserer Tabelle zu aktualisieren, führt Spark dann eine listFrom version 7-Operation aus, um die neuen Änderungen an der Tabelle anzuzeigen.
Anstatt alle zwischengeschalteten JSON-Dateien zu verarbeiten, kann Spark zur neuesten Checkpoint-Datei springen, da sie den gesamten Status der Tabelle bei Commit Nr. 10 enthält. Jetzt muss Spark nur noch die inkrementelle Verarbeitung von 0000011.json und 0000012.json durchführen, um den aktuellen Status der Tabelle zu erhalten. Spark speichert dann Version 12 der Tabelle im Speicher zwischen. Durch Befolgen dieses Workflows kann Delta Lake Spark verwenden, um den Status einer Tabelle jederzeit effizient zu aktualisieren.
Nachdem wir nun verstanden haben, wie das Delta Lake-Transaktionsprotokoll auf hoher Ebene funktioniert, wollen wir über Parallelität sprechen. Bisher haben unsere Beispiele hauptsächlich Szenarien behandelt, in denen Benutzer Transaktionen linear oder zumindest ohne Konflikte committen. Was passiert aber, wenn Delta Lake mit mehreren gleichzeitigen Lese- und Schreibvorgängen zu tun hat?
Die Antwort ist einfach. Da Delta Lake von Apache Spark unterstützt wird, ist es nicht nur möglich, dass mehrere Benutzer eine Tabelle gleichzeitig ändern – es wird auch erwartet. Um diese Situationen zu bewältigen, verwendet Delta Lake die optimistische Parallelitätskontrolle.
Die optimistische Parallelitätskontrolle ist eine Methode zum Umgang mit gleichzeitigen Transaktionen, die davon ausgeht, dass Transaktionen (Änderungen), die von verschiedenen Benutzern an einer Tabelle vorgenommen werden, abgeschlossen werden können, ohne miteinander in Konflikt zu geraten. Sie ist unglaublich schnell, da bei der Verarbeitung von Petabyte an Daten eine hohe Wahrscheinlichkeit besteht, dass Benutzer an völlig unterschiedlichen Teilen der Daten arbeiten, sodass sie nicht in Konflikt stehende Transaktionen gleichzeitig abschließen können.
Stellen Sie sich beispielsweise vor, Sie und ich arbeiten zusammen an einem Puzzlespiel. Solange wir beide an verschiedenen Teilen davon arbeiten – Sie beispielsweise an den Ecken und ich an den Kanten –, gibt es keinen Grund, warum wir nicht gleichzeitig an unserem Teil des größeren Puzzles arbeiten und das Puzzle doppelt so schnell fertigstellen können. Nur wenn wir die gleichen Teile zur gleichen Zeit benötigen, kommt es zu einem Konflikt. Das ist optimistische Parallelitätskontrolle.
Selbst bei optimistischer Parallelitätskontrolle versuchen Benutzer manchmal, dieselben Teile der Daten gleichzeitig zu ändern. Glücklicherweise verfügt Delta Lake über ein Protokoll dafür.
Um ACID-Transaktionen anzubieten, verfügt Delta Lake über ein Protokoll, um herauszufinden, wie Commits geordnet werden sollen (bekannt als das Konzept der Serialisierbarkeit in Datenbanken), und um zu bestimmen, was im Falle von zwei oder mehr gleichzeitig vorgenommenen Commits zu tun ist. Delta Lake behandelt diese Fälle durch die Implementierung einer Regel der gegenseitigen Ausschließung, und versucht dann, jeden Konflikt optimistisch zu lösen. Dieses Protokoll ermöglicht es Delta Lake, das ACID-Prinzip der Isolation zu erfüllen, das sicherstellt, dass der resultierende Zustand der Tabelle nach mehreren gleichzeitigen Schreibvorgängen derselbe ist, als ob diese Schreibvorgänge seriell, isoliert voneinander erfolgt wären.
Im Allgemeinen läuft der Prozess wie folgt ab:
Um zu sehen, wie sich das alles in Echtzeit abspielt, werfen wir einen Blick auf das Diagramm unten, um zu sehen, wie Delta Lake Konflikte verwaltet, wenn sie auftreten. Stellen Sie sich vor, zwei Benutzer lesen aus derselben Tabelle und versuchen dann jeweils, einige Daten hinzuzufügen.
000001.json aufgezeichnet werden kann.000001.json ausführen kann. Der Commit von Benutzer 1 wird akzeptiert, während der von Benutzer 2 abgelehnt wird.000002.json erfolgreich committet wird.In den allermeisten Fällen erfolgt diese Abstimmung im Hintergrund, nahtlos und erfolgreich. Sollte es jedoch ein unlösbares Problem geben, das Delta Lake nicht optimistisch lösen kann (z. B. wenn Benutzer 1 eine Datei gelöscht hat, die Benutzer 2 ebenfalls gelöscht hat), besteht die einzige Möglichkeit darin, einen Fehler auszugeben.
Abschließend sei gesagt, dass dieser Prozess die ACID-Eigenschaft der Dauerhaftigkeit erfüllt, da alle an Delta Lake-Tabellen vorgenommenen Transaktionen direkt auf der Festplatte gespeichert werden, was bedeutet, dass sie auch im Falle eines Systemausfalls bestehen bleiben.
Jede Tabelle ist das Ergebnis der Summe aller im Delta Lake-Transaktionsprotokoll aufgezeichneten Commits – nicht mehr und nicht weniger. Das Transaktionsprotokoll bietet eine Schritt-für-Schritt-Anleitung, die genau beschreibt, wie man vom ursprünglichen Zustand der Tabelle in ihren aktuellen Zustand gelangt.
Daher können wir den Zustand einer Tabelle zu jedem beliebigen Zeitpunkt wiederherstellen, indem wir mit einer Originaltabelle beginnen und nur Commits verarbeiten, die vor diesem Zeitpunkt vorgenommen wurden. Diese leistungsstarke Fähigkeit wird als „Zeitreisen“ oder Datenversionierung bezeichnet und kann in einer Reihe von Situationen lebensrettend sein. Weitere Informationen finden Sie im Blogbeitrag Einführung von Delta Time Travel für Data Lakes im großen Maßstab oder in der Delta Lake-Zeitreisedokumentation.
Als definitive Aufzeichnung jeder jemals an einer Tabelle vorgenommenen Änderung bietet das Delta Lake-Transaktionsprotokoll Benutzern eine überprüfbare Data Lineage, die für Governance-, Audit- und Compliance-Zwecke nützlich ist. Es kann auch verwendet werden, um den Ursprung einer unbeabsichtigten Änderung oder eines Fehlers in einer Pipeline bis zu der genauen Aktion zurückzuverfolgen, die ihn verursacht hat. Benutzer können DESCRIBE HISTORY ausführen, um Metadaten zu den vorgenommenen Änderungen anzuzeigen.
In diesem Blog sind wir auf die Details der Funktionsweise des Delta Lake-Transaktionsprotokolls eingegangen, einschließlich:
Artikel in dieser Serie:
Eintauchen in Delta Lake #1: Auspacken des Transaktionsprotokolls
Eintauchen in Delta Lake #2: Schema Enforcement & Evolution
Eintauchen in Delta Lake #3: DML-Interna (Aktualisieren, Löschen, Zusammenführen)
Verwandte Artikel:
Was ist ein Data Lake?
Produktionsreifes Machine Learning mit Delta Lake
(Dieser Blogbeitrag wurde mit KI-gestützten Tools übersetzt.) Originalbeitrag
