Einführung
Apache HBase ist der verteilte, versionierte Open-Source-Speichermanager von Hadoop, der sich gut für Random eignet , Lesen/Schreiben in Echtzeit Zugriff.
Warte warte? zufälliger Lese-/Schreibzugriff in Echtzeit?
Wie ist das möglich? Ist Hadoop nicht nur ein sequenzielles Lese-/Schreib-Stapelverarbeitungssystem?
Ja, wir reden über dasselbe, und in den nächsten Absätzen werde ich Ihnen erklären, wie HBase die zufällige E/A erreicht, wie es Daten speichert und die Entwicklung des HFile-Formats von HBase.
Apache Hadoop I/O-Dateiformate
Hadoop wird mit einem SequenceFile[1]-Dateiformat geliefert, das Sie verwenden können, um Ihre Schlüssel/Wert-Paare anzuhängen, aber aufgrund der Funktion nur zum Anhängen von hdfs kann das Dateiformat keine Änderung oder Entfernung eines eingefügten Werts zulassen. Die einzige zulässige Operation ist Anhängen, und wenn Sie einen bestimmten Schlüssel suchen möchten, müssen Sie die Datei durchlesen, bis Sie Ihren Schlüssel finden.
Wie Sie sehen können, sind Sie gezwungen, dem sequentiellen Lese-/Schreibmuster zu folgen … aber wie ist es möglich, darauf ein zufälliges Lese-/Schreibzugriffssystem mit niedriger Latenz wie HBase aufzubauen?
Um Ihnen bei der Lösung dieses Problems zu helfen, verfügt Hadoop über ein anderes Dateiformat namens MapFile[1], eine Erweiterung von SequenceFile. Das MapFile ist in Wirklichkeit ein Verzeichnis, das zwei SequenceFiles enthält:die Datendatei „/data“ und die Indexdatei „/index“. Mit MapFile können Sie sortierte Schlüssel/Wert-Paare anhängen und alle N Schlüssel (wobei N ein konfigurierbares Intervall ist) speichert es den Schlüssel und den Offset im Index. Dies ermöglicht eine recht schnelle Suche, da Sie nicht alle Datensätze scannen, sondern den Index scannen, der weniger Einträge enthält. Sobald Sie Ihren Block gefunden haben, können Sie in die echte Datendatei springen.
MapFile ist nett, weil Sie Schlüssel/Wert-Paare schnell nachschlagen können, aber es gibt immer noch zwei Probleme:
- Wie kann ich einen Schlüssel/Wert löschen oder ersetzen?
- Wenn meine Eingabe nicht sortiert ist, kann ich MapFile nicht verwenden.
HBase &MapFile
Der HBase-Schlüssel besteht aus:dem Zeilenschlüssel, der Spaltenfamilie, dem Spaltenqualifizierer, dem Zeitstempel und einem Typ.
Um das Problem des Löschens von Schlüssel/Wert-Paaren zu lösen, besteht die Idee darin, das Feld „Typ“ zu verwenden, um Schlüssel als gelöscht zu markieren (Tombstone-Marker). Um das Problem des Ersetzens von Schlüssel/Wert-Paaren zu lösen, müssen Sie nur den späteren Zeitstempel auswählen (der korrekte Wert befindet sich am Ende der Datei, nur anhängen bedeutet, dass der zuletzt eingefügte Wert am Ende ist).
Um das Problem der „nicht geordneten“ Schlüssel zu lösen, behalten wir die zuletzt hinzugefügten Schlüsselwerte im Speicher. Wenn Sie einen Schwellenwert erreicht haben, spült HBase ihn in eine MapFile. Auf diese Weise fügen Sie schließlich sortierte Schlüssel/Werte zu einer MapFile hinzu.
HBase macht genau das[2]:Wenn Sie mit table.put() einen Wert hinzufügen, wird Ihr Schlüssel/Wert zum MemStore hinzugefügt (unter der Haube ist MemStore eine sortierte ConcurrentSkipListMap). Wenn der Schwellenwert pro Memstore (hbase.hregion.memstore.flush.size) erreicht ist oder der RegionServer zu viel Arbeitsspeicher für Memstores verwendet (hbase.regionserver.global.memstore.upperLimit), werden die Daten als neue MapFile auf die Festplatte geleert .
Das Ergebnis jeder Leerung ist eine neue MapFile, und das bedeutet, dass Sie in mehr als einer Datei suchen müssen, um einen Schlüssel zu finden. Dies erfordert mehr Ressourcen und ist potenziell langsamer.
Jedes Mal, wenn ein get oder ein Scan ausgegeben wird, durchsucht HBase jede Datei, um das Ergebnis zu finden, um zu vermeiden, dass zu viele Dateien herumspringen, gibt es einen Thread, der erkennt, wenn Sie eine bestimmte Anzahl von Dateien erreicht haben (hbase.hstore.compaction .max). Es versucht dann, sie in einem Prozess namens Komprimierung zusammenzuführen, der im Grunde als Ergebnis der Dateizusammenführung eine neue große Datei erstellt.
HBase hat zwei Arten der Komprimierung:eine namens „Minor Compaction“, die einfach zwei oder mehr kleine Dateien zu einer zusammenführt, und die andere namens „Major Compaction“, die alle Dateien in der Region aufnimmt, sie zusammenführt und einige Aufräumarbeiten durchführt. Bei einer größeren Komprimierung werden gelöschte Schlüssel/Werte entfernt, diese neue Datei enthält keine Tombstone-Markierungen und alle doppelten Schlüssel/Werte (Operationen zum Ersetzen von Werten) werden entfernt.
Bis Version 0.20 hat HBase das MapFile-Format verwendet, um die Daten zu speichern, aber in 0.20 wurde ein neues HBase-spezifisches MapFile eingeführt (HBASE-61).
HFile v1
In HBase 0.20 wird MapFile durch HFile ersetzt:eine spezifische Zuordnungsdateiimplementierung für HBase. Die Idee ist MapFile ziemlich ähnlich, aber es fügt mehr Funktionen hinzu als nur eine einfache Schlüssel/Wert-Datei. Funktionen wie die Unterstützung von Metadaten und der Index werden jetzt in derselben Datei gespeichert.
Die Datenblöcke enthalten die eigentlichen Schlüssel/Werte als MapFile. Für jede „Block-Close-Operation“ wird der erste Schlüssel zum Index hinzugefügt, und der Index wird auf HFile close geschrieben.
Das HFile-Format fügt außerdem zwei zusätzliche „Metadaten“-Blocktypen hinzu:Meta und FileInfo. Diese beiden Schlüssel/Wert-Blöcke werden beim Schließen der Datei geschrieben.
Der Meta-Block ist darauf ausgelegt, eine große Datenmenge mit seinem Schlüssel als String zu speichern, während FileInfo eine einfache Map ist, die für kleine Informationen mit Schlüsseln und Werten bevorzugt wird, die beide Byte-Arrays sind. StoreFile von Regionserver verwendet Meta-Blöcke zum Speichern eines Bloom-Filters und FileInfo für Max SequenceId, Major Compaction Key und Timerange-Informationen. Diese Informationen sind nützlich, um das Lesen der Datei zu vermeiden, wenn der Schlüssel nicht vorhanden ist (Bloom-Filter), wenn die Datei zu alt ist (Max SequenceId) oder wenn die Datei zu neu ist (Timerange), um das zu enthalten, was wir suchen für.
HFile v2
In HBase 0.92 wurde das HFile-Format etwas geändert (HBASE-3857), um die Performance beim Speichern großer Datenmengen zu verbessern. Eines der Hauptprobleme mit HFile v1 besteht darin, dass Sie alle monolithischen Indizes und großen Bloom-Filter in den Speicher laden müssen, und um dieses Problem zu lösen, führt v2 mehrstufige Indizes und einen Bloom-Filter auf Blockebene ein. Infolgedessen bietet HFile v2 eine verbesserte Geschwindigkeit, Speicher- und Cache-Nutzung.
Das Hauptmerkmal dieser v2 sind „Inline-Blöcke“, die Idee ist, den Index und den Bloom-Filter pro Block zu unterbrechen, anstatt den gesamten Index und Bloom-Filter der gesamten Datei im Speicher zu haben. Auf diese Weise können Sie im RAM genau das behalten, was Sie brauchen.
Da der Index auf Blockebene verschoben wird, haben Sie einen mehrstufigen Index, dh jeder Block hat seinen eigenen Index (Blatt-Index). Der letzte Schlüssel jedes Blocks wird beibehalten, um den Zwischenwert/Index zu erstellen, der den Multilevel-Index b+tree ähnlich macht.
Der Block-Header enthält nun einige Informationen:Das Feld „Block Magic“ wurde durch das Feld „Block Type“ ersetzt, das den Inhalt des Blocks „Data“, Leaf-Index, Bloom, Metadata, Root-Index usw. beschreibt. Auch drei Felder (komprimierte/unkomprimierte Größe und Offset des vorherigen Blocks) wurden hinzugefügt, um eine schnelle Rückwärts- und Vorwärtssuche zu ermöglichen.
Datenblockkodierungen
Da Schlüssel sortiert und normalerweise sehr ähnlich sind, ist es möglich, eine bessere Komprimierung zu entwerfen, als es ein Allzweckalgorithmus tun kann.
HBASE-4218 hat versucht, dieses Problem zu lösen, und in HBase 0.94 können Sie zwischen einigen verschiedenen Algorithmen wählen:Prefix und Diff Encoding.
Die Hauptidee der Präfixcodierung besteht darin, das gemeinsame Präfix nur einmal zu speichern, da die Zeilen sortiert sind und der Anfang normalerweise gleich ist.
Die Diff-Codierung treibt dieses Konzept weiter voran. Anstatt den Schlüssel als undurchsichtige Folge von Bytes zu betrachten, teilt der Diff-Encoder jedes Schlüsselfeld auf, um jeden Teil besser zu komprimieren. Dies bedeutet, dass die Spaltenfamilie einmal gespeichert wird. Wenn die Schlüssellänge, die Wertlänge und der Typ mit der vorherigen Zeile identisch sind, wird das Feld weggelassen. Außerdem wird für eine erhöhte Komprimierung der Zeitstempel als Diff vom vorherigen gespeichert.
Beachten Sie, dass diese Funktion standardmäßig deaktiviert ist, da Schreiben und Scannen langsamer sind, aber mehr Daten zwischengespeichert werden. Um diese Funktion zu aktivieren, können Sie DATA_BLOCK_ENCODING =PREFIX | festlegen UNTERSCHIED | FAST_DIFF in der Tabelleninfo.
HFile v3
HBASE-5313 enthält einen Vorschlag zur Umstrukturierung des HFile-Layouts, um die Komprimierung zu verbessern:
- Packen Sie alle Schlüssel zusammen am Anfang des Blocks und alle Werte zusammen am Ende des Blocks. Auf diese Weise können Sie zwei verschiedene Algorithmen verwenden, um Schlüssel und Werte zu komprimieren.
- Komprimiere Zeitstempel mit dem XOR mit dem ersten Wert und verwende VInt statt long.
Außerdem wird ein Spaltenformat oder eine Spaltencodierung untersucht, werfen Sie einen Blick auf AVRO-806 für ein Spaltendateiformat von Doug Cutting.
Wie Sie vielleicht sehen, geht der Trend in der Entwicklung dahin, sich bewusster darüber zu werden, was die Datei enthält, um eine bessere Komprimierung oder eine bessere Standorterkennung zu erhalten, was dazu führt, dass weniger Daten von der Festplatte geschrieben/gelesen werden müssen. Weniger E/A bedeutet mehr Geschwindigkeit!
[1] https://clouderatemp.wpengine.com/blog/2011/01/hadoop-io-sequence-map-set-array-bloommap-files/
[2] https://clouderatemp.wpengine. com/blog/2012/06/hbase-write-path/