Erfahren Sie mehr über die Designentscheidungen hinter der neuen Unterstützung von HBase für MOBs.
Apache HBase ist eine verteilte, skalierbare, leistungsstarke und konsistente Schlüsselwertdatenbank, die eine Vielzahl binärer Datentypen speichern kann. Es zeichnet sich dadurch aus, dass es viele relativ kleine Werte (<10 KB) speichert und Lese- und Schreibvorgänge mit geringer Latenz bereitstellt.
Es besteht jedoch eine wachsende Nachfrage nach dem Speichern von Dokumenten, Bildern und anderen moderaten Objekten (MOBs) in HBase bei gleichzeitig niedriger Latenz für Lese- und Schreibvorgänge. Ein solcher Anwendungsfall ist eine Bank, die signierte und gescannte Kundendokumente speichert. Als weiteres Beispiel möchten Transportunternehmen möglicherweise Momentaufnahmen von Verkehr und fahrenden Autos speichern. Diese MOBs sind im Allgemeinen einmal beschreibbar.
Leider kann sich die Leistung in Situationen verschlechtern, in denen viele mittelgroße Werte (100 KB bis 10 MB) aufgrund des ständig steigenden E/A-Drucks gespeichert werden, der durch Komprimierungen entsteht. Stellen Sie sich den Fall vor, in dem täglich 1 TB Fotos von Verkehrskameras mit einer Größe von jeweils 1 MB in HBase gespeichert werden. Teile der gespeicherten Dateien werden mehrfach durch kleinere Komprimierungen komprimiert und schließlich werden Daten durch größere Komprimierungen neu geschrieben. Zusammen mit der Anhäufung dieser MOBs verlangsamt die durch Komprimierungen erzeugte I/O die Komprimierungen, weitere Block-Memstore-Flushing und blockiert schließlich Aktualisierungen. Ein großer MOB-Speicher löst häufige Regionsaufteilungen aus, wodurch die Verfügbarkeit der betroffenen Regionen verringert wird.
Um diese Nachteile zu beheben, haben Cloudera- und Intel-Ingenieure MOB-Unterstützung in einem HBase-Zweig implementiert (hbase-11339:HBase MOB). Dieser Zweig wird mit dem Master in HBase 1.1 oder 1.2 zusammengeführt und ist bereits vorhanden und wird auch in CDH 5.4.x unterstützt.
Vorgänge auf MOBs sind normalerweise schreibintensiv, mit seltenen Aktualisierungen oder Löschungen und relativ seltenen Lesevorgängen. MOBs werden normalerweise zusammen mit ihren Metadaten gespeichert. Metadaten, die sich auf MOBs beziehen, können beispielsweise Fahrzeugnummer, Geschwindigkeit und Farbe umfassen. Metadaten sind relativ zu den MOBs sehr klein. Auf Metadaten wird normalerweise zu Analysezwecken zugegriffen, während auf MOBs normalerweise nur zufällig zugegriffen wird, wenn sie explizit mit Zeilenschlüsseln angefordert werden.
Benutzer möchten die MOBs in HBase mit geringer Latenz in denselben APIs lesen und schreiben und wollen starke Konsistenz, Sicherheit, Snapshots und HBase-Replikation zwischen Clustern und so weiter. Um diese Ziele zu erreichen, wurden MOBs aus dem Haupt-E/A-Pfad von HBase in einen neuen E/A-Pfad verschoben.
In diesem Beitrag erfahren Sie mehr über diesen Designansatz und warum er ausgewählt wurde.
Mögliche Ansätze
Es gab ein paar mögliche Ansätze für dieses Problem. Der erste Ansatz, den wir in Betracht gezogen haben, bestand darin, MOBs in HBase mit abgestimmten Aufteilungs- und Komprimierungsrichtlinien zu speichern – eine größere gewünschteMaxFileSize verringert die Häufigkeit der Regionsaufteilung, und weniger oder keine Komprimierungen können die Schreibverstärkungsstrafe vermeiden. Dieser Ansatz würde die Schreiblatenz und den Durchsatz erheblich verbessern. Zusammen mit der zunehmenden Anzahl gespeicherter Dateien gäbe es jedoch zu viele geöffnete Lesegeräte in einem einzelnen Speicher, sogar mehr als vom Betriebssystem zugelassen. Infolgedessen würde viel Speicher verbraucht und die Leseleistung würde sich verschlechtern.
Ein anderer Ansatz bestand darin, ein HBase + HDFS-Modell zu verwenden, um die Metadaten und MOBs separat zu speichern. In diesem Modell wird eine einzelne Datei durch einen Eintrag in HBase verknüpft. Dies ist eine Client-Lösung, und die Transaktion wird vom Client gesteuert – keine HBase-seitigen Speicher werden von MOBs verbraucht. Dieser Ansatz würde für Objekte mit mehr als 50 MB funktionieren, aber für MOBs führen viele kleine Dateien zu einer ineffizienten HDFS-Nutzung, da die Standardblockgröße in HDFS 128 MB beträgt.
Nehmen wir zum Beispiel an, ein NameNode hat 48 GB Speicher und jede Datei ist 100 KB groß mit drei Replikaten. Jede Datei benötigt mehr als 300 Byte Speicher, sodass ein NameNode mit 48 GB Speicher etwa 160 Millionen Dateien aufnehmen kann, was uns auf die Speicherung von insgesamt nur 16 TB MOB-Dateien beschränken würde.
Als Verbesserung hätten wir die kleinen MOB-Dateien zu größeren zusammenfügen können – das heißt, eine Datei könnte mehrere MOB-Einträge haben – und den Offset und die Länge zum schnellen Lesen in der HBase-Tabelle speichern. Es ist jedoch schwierig, die Datenkonsistenz aufrechtzuerhalten und gelöschte MOBs und kleine MOB-Dateien in Komprimierungen zu verwalten.
Wenn wir diesen Ansatz verwenden würden, müssten wir außerdem neue Sicherheitsrichtlinien in Betracht ziehen, die Atomizitätseigenschaften von Schreibvorgängen verlieren und möglicherweise die durch Replikation und Snapshots bereitgestellte Sicherung und Notfallwiederherstellung verlieren.
HBase-MOB-Design
Da die meisten Bedenken beim Speichern von MOBs in HBase die durch Komprimierungen erzeugten I/Os betreffen, lag der Schlüssel am Ende darin, MOBs aus der Verwaltung durch normale Regionen zu entfernen, um Regionsaufteilungen und Komprimierungen dort zu vermeiden.
Das HBase MOB-Design ähnelt dem HBase + HDFS-Ansatz, da wir die Metadaten und MOBs separat speichern. Der Unterschied liegt jedoch in einem serverseitigen Design:Memstore speichert die MOBs zwischen, bevor sie auf die Festplatte geflusht werden, die MOBs werden bei jedem Flush in eine HFile namens „MOB-Datei“ geschrieben, und jede MOB-Datei hat mehrere Einträge anstelle einer einzelnen Datei in HDFS für jeden MOB. Diese MOB-Datei wird in einer speziellen Region gespeichert. Alle Lese- und Schreibvorgänge können von den aktuellen HBase-APIs verwendet werden.
Schreiben und lesen
Jeder MOB hat einen Schwellwert:Wenn die Wertelänge einer Zelle größer als dieser Schwellwert ist, wird diese Zelle als MOB-Zelle betrachtet.
Wenn die MOB-Zellen in den Regionen aktualisiert werden, werden sie genau wie die normalen Zellen in die WAL und den Speicher geschrieben. Beim Leeren werden die MOBs in MOB-Dateien geleert und die Metadaten und Pfade von MOB-Dateien werden geleert, um Dateien zu speichern. Die Datenkonsistenz- und HBase-Replikationsfunktionen sind für dieses Design nativ.
Die MOB-Bearbeitungen sind größer als üblich. Bei der Synchronisierung ist auch der entsprechende I/O größer, was die Synchronisierungsoperationen von WAL verlangsamen kann. Wenn es andere Regionen gibt, die dieselbe WAL teilen, kann die Schreiblatenz dieser Regionen beeinträchtigt werden. Wenn jedoch Datenkonsistenz und Nichtflüchtigkeit erforderlich sind, ist WAL ein Muss.
Den Zellen wird erlaubt, sich zwischen gespeicherten Dateien und MOB-Dateien in den Verdichtungen zu bewegen, indem der Schwellenwert geändert wird. Der Standardschwellenwert beträgt 100 KB.
Wie unten dargestellt, werden die Zellen, die die Pfade von MOB-Dateien enthalten, als Referenzzellen bezeichnet . Die Tags bleiben in den Zellen erhalten, sodass wir uns weiterhin auf den HBase-Sicherheitsmechanismus verlassen können.
Die Referenzzellen haben Referenz-Tags, die sie von normalen Zellen unterscheiden. Ein Referenz-Tag impliziert eine MOB-Zelle in einer MOB-Datei, und daher ist beim Lesen eine weitere Auflösung erforderlich.
Beim Lesen öffnet der Speicherscanner Scanner zum Speichern und Speichern von Dateien. Wenn eine Referenzzelle erreicht wird, liest der Scanner den Dateipfad aus dem Zellenwert und sucht denselben Zeilenschlüssel aus dieser Datei. Der Block-Cache kann für die MOB-Dateien im Scan aktiviert werden, was die Suche beschleunigen kann.
Es ist nicht erforderlich, Reader für alle MOB-Dateien zu öffnen; Bei Bedarf wird nur einer benötigt. Dieses zufällige Lesen wird nicht von der Anzahl der MOB-Dateien beeinflusst. Wir müssen die MOB-Dateien also nicht immer wieder komprimieren, wenn sie groß genug sind.
Der MOB-Dateiname ist lesbar und besteht aus drei Teilen:dem MD5 des Startschlüssels, dem letzten Datum der Zellen in dieser MOB-Datei und einer UUID. Der erste Teil ist der Startschlüssel der Region, aus der diese MOB-Datei geleert wird. Normalerweise haben die MOBs eine benutzerdefinierte TTL, sodass Sie abgelaufene MOB-Dateien finden und löschen können, indem Sie den zweiten Teil mit der TTL vergleichen.
Schnappschuss
Um Snapshot-freundlicher zu sein, werden die MOB-Dateien in einer speziellen Dummy-Region gespeichert, wodurch Snapshot, Tabellenexport/-klon und Archiv wie erwartet funktionieren.
Beim Speichern eines Snapshots in einer Tabelle erstellt man die MOB-Region im Snapshot und fügt die vorhandenen MOB-Dateien zum Manifest hinzu. Erstellen Sie beim Wiederherstellen des Snapshots Dateiverknüpfungen in der MOB-Region.
Reinigen und Verdichten
Es gibt zwei Situationen, in denen MOB-Dateien gelöscht werden sollten:wenn die MOB-Datei abgelaufen ist und wenn die MOB-Datei zu klein ist und mit größeren zusammengeführt werden sollte, um die HDFS-Effizienz zu verbessern.
HBase MOB hat eine Aufgabe im Master:Es scannt die MOB-Dateien, findet die abgelaufenen, die durch das Datum im Dateinamen bestimmt werden, und löscht sie. Auf diese Weise wird regelmäßig Speicherplatz zurückgewonnen, indem abgelaufene MOB-Dateien altern.
MOB-Dateien können im Vergleich zu einem HDFS-Block relativ klein sein, wenn Sie Zeilen schreiben, in denen nur wenige Einträge als MOBs gelten; Außerdem können gelöschte Zellen vorhanden sein. Sie müssen die gelöschten Zellen löschen und die kleinen Dateien zu größeren zusammenführen, um die HDFS-Nutzung zu verbessern. Die MOB-Komprimierungen komprimieren nur die kleinen Dateien und die großen Dateien werden nicht berührt, wodurch eine wiederholte Komprimierung zu großen Dateien vermieden wird.
Einige andere Dinge, die Sie beachten sollten:
- Wissen, welche Zellen gelöscht werden. Bei jeder größeren Komprimierung von HBase werden die Löschmarkierungen in eine del-Datei geschrieben, bevor sie gelöscht werden.
- Im ersten Schritt der MOB-Komprimierung werden diese del-Dateien zu größeren zusammengeführt.
- Alle kleinen MOB-Dateien sind ausgewählt. Wenn die Anzahl der kleinen Dateien gleich der Anzahl der vorhandenen MOB-Dateien ist, wird diese Komprimierung als eine größere angesehen und als ALL_FILES-Komprimierung bezeichnet.
- Diese ausgewählten Dateien werden durch den Startschlüssel und das Datum im Dateinamen partitioniert. Die kleinen Dateien in jeder Partition werden mit del-Dateien komprimiert, sodass gelöschte Zellen gelöscht werden können; In der Zwischenzeit wird eine neue HFile mit neuen Referenzzellen generiert, der Compactor schreibt die neue MOB-Datei fest und lädt diese HFile dann massenweise in HBase.
- Nachdem die Komprimierungen in allen Partitionen abgeschlossen sind, wenn eine ALL_FILES-Komprimierung beteiligt ist, werden die del-Dateien archiviert.
Der Lebenszyklus von MOB-Dateien ist unten dargestellt. Grundsätzlich werden sie erstellt, wenn der Speicher geleert wird, und von HFileCleaner aus dem Dateisystem gelöscht, wenn sie nicht vom Snapshot referenziert werden oder im Archiv abgelaufen sind.
Schlussfolgerung
Zusammenfassend lässt sich sagen, dass das neue HBase-MOB-Design MOBs aus dem Haupt-E/A-Pfad von HBase entfernt, während die meisten Sicherheits-, Komprimierungs- und Snapshot-Funktionen beibehalten werden. Es berücksichtigt die Eigenschaften von Operationen in MOB, macht die Schreibverstärkung von MOBs vorhersehbarer und hält niedrige Latenzen sowohl beim Lesen als auch beim Schreiben.
Jincheng Du ist Software Engineer bei Intel und ein HBase-Mitarbeiter.
Jon Hsieh ist Software Engineer bei Cloudera und HBase-Committer/PMC-Mitglied. Er ist auch der Gründer von Apache Flume und ein Committer für Apache Sqoop.