HBase
 sql >> Datenbank >  >> NoSQL >> HBase

Aufteilen und Zusammenführen von Apache HBase-Regionen

Dieser Blogbeitrag wurde vor der Fusion mit Cloudera auf Hortonworks.com veröffentlicht. Einige Links, Ressourcen oder Referenzen sind möglicherweise nicht mehr korrekt.

Für diesen Beitrag tauchen wir technisch tief in einen der Kernbereiche von HBase ein. Insbesondere werden wir uns ansehen, wie Apache HBase die Last über Regionen verteilt und die Aufteilung von Regionen verwaltet. HBase speichert Datenzeilen in Tabellen. Tabellen sind in Zeilenblöcke unterteilt, die als „Regionen“ bezeichnet werden. Diese Regionen werden über den Cluster verteilt, gehostet und durch den RegionServer-Prozess für Client-Prozesse verfügbar gemacht. Eine Region ist ein kontinuierlicher Bereich innerhalb des Schlüsselraums, d. h. alle Zeilen in der Tabelle, die zwischen dem Startschlüssel und dem Endschlüssel der Region sortiert sind, werden in derselben Region gespeichert. Regionen sind nicht überlappend, d. h. ein einzelner Zeilenschlüssel gehört zu jedem Zeitpunkt zu genau einer Region. Eine Region wird zu jedem Zeitpunkt nur von einem einzigen Regionsserver bedient, wodurch HBase eine starke Konsistenz innerhalb einer einzelnen Zeile garantiert#. Zusammen mit dem -ROOT- und .META. Regionen bilden die Regionen einer Tabelle effektiv einen B-Baum mit 3 Ebenen, um eine Zeile innerhalb einer Tabelle zu lokalisieren.

Eine Region wiederum besteht aus vielen „Stores“, die Spaltenfamilien entsprechen. Ein Speicher enthält einen Speicher und null oder mehr Speicherdateien. Die Daten für jede Spaltenfamilie werden separat gespeichert und abgerufen.

Eine Tabelle besteht normalerweise aus vielen Regionen, die wiederum von vielen Regionsservern gehostet werden. Somit sind Regionen der physische Mechanismus, der verwendet wird, um die Schreib- und Abfragelast auf Regionsserver zu verteilen. Wenn eine Tabelle zum ersten Mal erstellt wird, weist HBase der Tabelle standardmäßig nur eine Region zu. Das bedeutet, dass zunächst alle Anfragen an einen einzelnen Regionsserver gehen, unabhängig von der Anzahl der Regionsserver. Dies ist der Hauptgrund, warum anfängliche Phasen des Ladens von Daten in eine leere Tabelle nicht die gesamte Kapazität des Clusters nutzen können.

Vorabaufteilung

Der Grund, warum HBase nur einen Bereich für die Tabelle erstellt, liegt darin, dass es möglicherweise nicht wissen kann, wie die Teilungspunkte innerhalb des Zeilenschlüsselraums erstellt werden sollen. Das Treffen solcher Entscheidungen basiert stark auf der Verteilung der Schlüssel in Ihren Daten. Anstatt zu raten und Sie mit den Konsequenzen fertig zu werden, stellt Ihnen HBase Tools zur Verfügung, mit denen Sie dies vom Client aus verwalten können. Mit einem Prozess namens Pre-Splitting können Sie eine Tabelle mit vielen Regionen erstellen, indem Sie die Teilungspunkte zum Zeitpunkt der Tabellenerstellung angeben. Da das Pre-Splitting dafür sorgt, dass die Anfangslast gleichmäßiger im Cluster verteilt wird, sollten Sie es immer in Betracht ziehen, wenn Sie Ihre Schlüsselverteilung vorher kennen. Allerdings birgt das Pre-Splitting auch das Risiko, Regionen zu erstellen, die die Last aufgrund von Datenverzerrung oder bei Vorhandensein sehr heißer oder großer Zeilen nicht wirklich gleichmäßig verteilen. Wenn der anfängliche Satz von Regionsaufteilungspunkten schlecht gewählt wird, kann es am Ende zu einer heterogenen Lastverteilung kommen, die wiederum die Leistung Ihres Clusters einschränkt.

Es gibt keine kurze Antwort auf die optimale Anzahl von Regionen für eine bestimmte Last, aber Sie können mit einem niedrigeren Vielfachen der Anzahl von Regionsservern als Anzahl der Aufteilungen beginnen und dann die automatische Aufteilung den Rest erledigen lassen.

Ein Problem beim Pre-Splitting ist die Berechnung der Split-Punkte für die Tabelle. Sie können das Dienstprogramm RegionSplitter verwenden. RegionSplitter erstellt die Teilungspunkte mithilfe eines austauschbaren SplitAlgorithm. HexStringSplit und UniformSplit sind zwei vordefinierte Algorithmen. Ersteres kann verwendet werden, wenn die Zeilenschlüssel ein Präfix für hexadezimale Zeichenfolgen haben (z. B. wenn Sie Hashes als Präfixe verwenden). Letzteres teilt den Schlüsselraum gleichmäßig auf, vorausgesetzt, es handelt sich um zufällige Byte-Arrays. Sie können auch Ihren benutzerdefinierten SplitAlgorithm implementieren und ihn vom RegionSplitter-Dienstprogramm verwenden.

$ hbase org.apache.hadoop.hbase.util.RegionSplitter test_table HexStringSplit -c 10 -f f1

Dabei gibt -c 10 die angeforderte Anzahl von Regionen als 10 an und -f gibt die Spaltenfamilien an, die Sie in der Tabelle haben möchten, getrennt durch „:“. Das Tool erstellt eine Tabelle namens „test_table“ mit 10 Regionen:

13/01/18 18:49:32 DEBUG hbase.HRegionInfo: Current INFO from scan results = {NAME => 'test_table,,1358563771069.acc1ad1b7962564fc3a43e5907e8db33.', STARTKEY => '', ENDKEY => '19999999', ENCODED => acc1ad1b7962564fc3a43e5907e8db33,}
13/01/18 18:49:32 DEBUG hbase.HRegionInfo: Current INFO from scan results = {NAME => 'test_table,19999999,1358563771096.37ec12df6bd0078f5573565af415c91b.', STARTKEY => '19999999', ENDKEY => '33333332', ENCODED => 37ec12df6bd0078f5573565af415c91b,}
...

Wenn Sie Split-Punkte zur Hand haben, können Sie auch die HBase-Shell verwenden, um die Tabelle mit den gewünschten Split-Punkten zu erstellen.

hbase(main):015:0> create 'test_table', 'f1', SPLITS=> ['a', 'b', 'c']

oder

$ echo -e  "anbnc" >/tmp/splits
hbase(main):015:0> create 'test_table', 'f1', SPLITSFILE=>'/tmp/splits'

Für eine optimale Lastverteilung sollten Sie über Ihr Datenmodell und die Schlüsselverteilung nachdenken, um den richtigen Teilungsalgorithmus oder die richtigen Teilungspunkte auszuwählen. Unabhängig von der Methode, die Sie zum Erstellen der Tabelle mit einer vordefinierten Anzahl von Regionen gewählt haben, können Sie jetzt mit dem Laden der Daten in die Tabelle beginnen und sehen, dass die Last über Ihren Cluster verteilt ist. Sie können die automatische Aufteilung übernehmen lassen, sobald die Datenerfassung beginnt, und die Gesamtzahl der Regionen für die Tabelle kontinuierlich überwachen.

Automatisches Teilen

Unabhängig davon, ob Pre-Splitting verwendet wird oder nicht, sobald eine Region eine bestimmte Grenze erreicht, wird sie automatisch in zwei Regionen geteilt. Wenn Sie HBase 0.94 (im Lieferumfang von HDP-1.2 enthalten) verwenden, können Sie konfigurieren, wann HBase entscheidet, eine Region aufzuteilen, und wie die Aufteilungspunkte über die austauschbare RegionSplitPolicy-API berechnet werden. Es gibt ein paar vordefinierte Regionenaufteilungsrichtlinien:ConstantSizeRegionSplitPolicy, IncreasingToUpperBoundRegionSplitPolicy und KeyPrefixRegionSplitPolicy.

Die erste ist die standardmäßige und einzige Aufteilungsrichtlinie für HBase-Versionen vor 0.94. Es teilt die Regionen auf, wenn die Gesamtdatengröße für einen der Speicher (entsprechend einer Spaltenfamilie) in der Region größer wird als die konfigurierte „hbase.hregion.max.filesize“, die einen Standardwert von 10 GB hat. Diese Aufteilungsrichtlinie ist ideal in Fällen, in denen Sie eine Voraufteilung vorgenommen haben und an einer geringeren Anzahl von Regionen pro Regionsserver interessiert sind.

Die standardmäßige Aufteilungsrichtlinie für HBase 0.94 und Trunk ist IncreasingToUpperBoundRegionSplitPolicy, die eine aggressivere Aufteilung basierend auf der Anzahl der Regionen vornimmt, die auf demselben Regionsserver gehostet werden. Die Aufteilungsrichtlinie verwendet die maximale Speicherdateigröße basierend auf Min (R^2 * „hbase.hregion.memstore.flush.size“, „hbase.hregion.max.filesize“), wobei R die Anzahl der Regionen derselben ist Tabelle, die auf demselben Regionserver gehostet wird. So wird beispielsweise mit der standardmäßigen Memstore-Flush-Größe von 128 MB und der standardmäßigen maximalen Speichergröße von 10 GB die erste Region auf dem Regionsserver direkt nach der ersten Flush bei 128 MB geteilt. Wenn die Anzahl der auf dem Regionsserver gehosteten Regionen zunimmt, werden zunehmende Aufteilungsgrößen verwendet:512 MB, 1152 MB, 2 GB, 3,2 GB, 4,6 GB, 6,2 GB usw. Nach Erreichen von 9 Regionen geht die Aufteilungsgröße über die konfigurierte „hbase .hregion.max.filesize“, ab diesem Zeitpunkt wird 10 GB Splitgröße verwendet. Unabhängig davon, wann die Teilung erfolgt, ist der verwendete Teilungspunkt für diese beiden Algorithmen der Zeilenschlüssel, der dem Mittelpunkt im „Blockindex“ für die größte Geschäftsdatei im größten Geschäft entspricht.

KeyPrefixRegionSplitPolicy ist eine merkwürdige Ergänzung des HBase-Arsenals. Sie können die Länge des Präfixes für Ihre Zeilenschlüssel konfigurieren, um sie zu gruppieren, und diese Teilungsrichtlinie stellt sicher, dass die Regionen nicht mitten in einer Gruppe von Zeilen mit demselben Präfix geteilt werden. Wenn Sie Präfixe für Ihre Schlüssel festgelegt haben, können Sie diese Aufteilungsrichtlinie verwenden, um sicherzustellen, dass Zeilen mit demselben Zeilenschlüsselpräfix immer in derselben Region landen. Diese Gruppierung von Datensätzen wird manchmal als „Entitätsgruppen“ oder „Zeilengruppen“ bezeichnet. Dies ist eine Schlüsselfunktion, wenn Sie die Verwendung der Funktion „Lokale Transaktionen“ (alternativer Link) in Ihrem Anwendungsdesign in Betracht ziehen.

Sie können die zu verwendende standardmäßige Aufteilungsrichtlinie konfigurieren, indem Sie die Konfiguration „hbase.regionserver.region.split.policy“ festlegen oder den Tabellendeskriptor konfigurieren. Für Sie mutige Seelen können Sie auch Ihre eigene benutzerdefinierte Aufteilungsrichtlinie implementieren und diese zum Zeitpunkt der Tabellenerstellung oder durch Ändern einer vorhandenen Tabelle einfügen:

HTableDescriptor tableDesc = new HTableDescriptor("example-table");
tableDesc.setValue(HTableDescriptor.SPLIT_POLICY, AwesomeSplitPolicy.class.getName());
//add columns etc
admin.createTable(tableDesc);

Wenn Sie eine Vorabaufteilung durchführen und Regionsaufteilungen manuell verwalten möchten, können Sie Regionsaufteilungen auch deaktivieren, indem Sie „hbase.hregion.max.filesize“ auf eine hohe Zahl und die Aufteilungsrichtlinie auf ConstantSizeRegionSplitPolicy setzen. Sie sollten jedoch einen Sicherheitswert von etwa 100 GB verwenden, damit Regionen nicht über die Kapazitäten eines Regionsservers hinauswachsen. Sie können erwägen, die automatische Aufteilung zu deaktivieren und sich auf den anfänglichen Satz von Regionen aus der Voraufteilung verlassen, wenn Sie beispielsweise einheitliche Hashes für Ihre Schlüsselpräfixe verwenden, und Sie können sicherstellen, dass die Lese-/Schreiblast für jede Region sowie ihre Größe gewährleistet ist ist in allen Regionen der Tabelle einheitlich.

Erzwungene Trennungen

HBase ermöglicht es Clients auch, das Teilen einer Online-Tabelle von der Clientseite aus zu erzwingen. Beispielsweise kann die HBase-Shell verwendet werden, um alle Bereiche der Tabelle zu teilen oder einen Bereich zu teilen, optional durch Angabe eines Teilungspunkts.

hbase(main):024:0> split 'b07d0034cbe72cb040ae9cf66300a10c', 'b'
0 row(s) in 0.1620 seconds

Wenn Sie bei sorgfältiger Überwachung Ihrer HBase-Lastverteilung feststellen, dass einige Regionen ungleichmäßige Lasten erhalten, können Sie erwägen, diese Regionen manuell aufzuteilen, um die Last auszugleichen und den Durchsatz zu verbessern. Ein weiterer Grund, warum Sie manuelle Aufteilungen vornehmen möchten, ist, wenn Sie feststellen, dass die anfänglichen Aufteilungen für die Region nicht optimal sind und Sie automatische Aufteilungen deaktiviert haben. Das kann zum Beispiel passieren, wenn sich die Datenverteilung im Laufe der Zeit ändert.

Wie Regionsaufteilungen implementiert werden

Wenn Schreibanforderungen vom Regionsserver verarbeitet werden, sammeln sie sich in einem In-Memory-Speichersystem namens „Memstore“ an. Sobald der Speicher gefüllt ist, wird sein Inhalt als zusätzliche Speicherdateien auf die Platte geschrieben. Dieses Ereignis wird als „Memstore Flush“ bezeichnet. Wenn sich Speicherdateien ansammeln, „komprimiert“ der RegionServer sie zu kombinierten, größeren Dateien. Nachdem jede Leerung oder Komprimierung abgeschlossen ist, wird eine Regionsaufteilungsanforderung in die Warteschlange eingereiht, wenn die RegionSplitPolicy entscheidet, dass die Region in zwei Teile geteilt werden soll. Da alle Datendateien in HBase unveränderlich sind, schreiben die neu erstellten Tochterregionen bei einer Teilung nicht alle Daten in neue Dateien um. Stattdessen erstellen sie kleine Sym-Link-ähnliche Dateien mit dem Namen Referenzdateien, die je nach Teilungspunkt entweder auf den oberen oder unteren Teil der übergeordneten Speicherdatei verweisen. Die Referenzdatei wird wie eine normale Datendatei verwendet, aber nur die Hälfte der Datensätze. Die Region kann nur geteilt werden, wenn keine Verweise mehr auf die unveränderlichen Datendateien der übergeordneten Region vorhanden sind. Diese Referenzdateien werden nach und nach durch Komprimierungen bereinigt, sodass die Region aufhört, auf ihre übergeordneten Dateien zu verweisen, und weiter aufgeteilt werden kann.

Obwohl die Teilung der Region eine lokale Entscheidung ist, die auf dem RegionServer getroffen wird, muss der Teilungsprozess selbst mit vielen Akteuren koordiniert werden. Der RegionServer benachrichtigt den Master vor und nach der Teilung, aktualisiert die .META. Tabelle, damit Clients die neuen Tochterregionen erkennen können, und ordnet die Verzeichnisstruktur und Datendateien in HDFS neu an. Split ist ein Multi-Task-Prozess. Um im Fehlerfall ein Rollback zu ermöglichen, führt der RegionServer ein In-Memory-Journal über den Ausführungsstatus. Die Schritte, die der RegionServer zum Ausführen der Aufteilung unternimmt, sind in Abbildung 1 dargestellt. Jeder Schritt ist mit seiner Schrittnummer gekennzeichnet. Aktionen von RegionServers oder Master werden in Rot angezeigt, während Aktionen von den Clients in Grün angezeigt werden.

1. RegionServer entscheidet lokal über die Teilung der Region und bereitet die Teilung vor. Als erster Schritt erstellt es einen znode im Zookeeper unter /hbase/region-in-transition/region-name im SPLITTING-Zustand.
2. Der Master erfährt von diesem Znode, da er einen Beobachter für den Eltern-Region-in-Transition-Znode hat.
3. RegionServer erstellt ein Unterverzeichnis mit dem Namen „.splits“ unter dem Regionsverzeichnis des übergeordneten Elements in HDFS.
4. RegionServer schließt die übergeordnete Region, erzwingt eine Leerung des Caches und markiert die Region in ihren lokalen Datenstrukturen als offline. An diesem Punkt lösen Clientanforderungen, die in die übergeordnete Region gelangen, NotServingRegionException aus. Der Client wird es mit einem gewissen Backoff erneut versuchen.
5. RegionServer erstellt die Regionsverzeichnisse unter dem Verzeichnis .splits für Tochterregionen A und B und erstellt die erforderlichen Datenstrukturen. Dann teilt es die Geschäftsdateien in dem Sinne auf, dass es zwei Referenzdateien pro Geschäftsdatei in der übergeordneten Region erstellt. Diese Referenzdateien verweisen auf die Dateien der übergeordneten Regionen.
6. RegionServer erstellt das tatsächliche Regionsverzeichnis in HDFS und verschiebt die Referenzdateien für jede Tochter.
7. RegionServer sendet eine Put-Anfrage an die .META. -Tabelle und legt das übergeordnete Element in der .META-Datei als offline fest. Tabelle und fügt Informationen über Tochterregionen hinzu. Zu diesem Zeitpunkt gibt es keine individuellen Einträge in .META. für die Töchter. Clients sehen, dass die Elternregion geteilt ist, wenn sie .META. scannen, wissen aber nichts über die Töchter, bis sie in .META. erscheinen. erfolgreich ist, wird der Elternteil effektiv geteilt. Wenn der RegionServer fehlschlägt, bevor dieser RPC erfolgreich ist, bereinigen der Master und der nächste Regionsserver, der die Region öffnet, den Dirty-State über die Regionsaufteilung. Nach dem .META. Update wird die Regionsaufteilung jedoch vom Master aktualisiert.
8. RegionServer öffnet Töchter parallel, um Schreibvorgänge zu akzeptieren.
9. RegionServer fügt die Töchter A und B zu .META hinzu. zusammen mit der Information, dass es die Regionen beherbergt. Nach diesem Punkt können Clients die neuen Regionen erkennen und Anforderungen an die neue Region senden. Clients cachen die .META. Einträge lokal, aber wenn sie Anfragen an den Regionsserver oder .META. stellen, werden ihre Caches ungültig und sie erfahren von .META.. über die neuen Regionen.
10. RegionServer aktualisiert znode /hbase/region-in-transition/region-name in zookeeper auf SPLIT, damit der Master davon erfahren kann. Der Balancer kann die Tochterregionen nach Belieben anderen Regionsservern zuweisen.
11. Nach der Teilung enthalten Meta und HDFS immer noch Verweise auf die übergeordnete Region. Diese Verweise werden entfernt, wenn Verdichtungen in Tochterregionen die Datendateien neu schreiben. Garbage-Collection-Tasks im Master prüfen periodisch, ob die Tochterregionen noch auf Elterndateien verweisen. Wenn nicht, wird die übergeordnete Region entfernt.

Regionszusammenführungen

Anders als beim Aufteilen von Regionen bietet HBase an dieser Stelle keine brauchbaren Tools zum Zusammenführen von Regionen. Obwohl es HMerge- und Merge-Tools gibt, sind sie für den allgemeinen Gebrauch nicht sehr geeignet. Derzeit gibt es keine Unterstützung für Online-Tabellen und Funktionen zum automatischen Zusammenführen. Bei Problemen wie OnlineMerge, vom Master initiierten automatischen Regionszusammenführungen, ZK-basierten Lese-/Schreibsperren für Tabellenoperationen arbeiten wir jedoch daran, Regionsaufteilungen zu stabilisieren und eine bessere Unterstützung für Regionszusammenführungen zu ermöglichen. Bleiben Sie dran!

Schlussfolgerung

Wie Sie sehen können, erledigt HBase unter der Haube viel Haushalt, um Regionsaufteilungen zu verwalten und automatisiertes Sharding durch Regionen durchzuführen. HBase bietet jedoch auch die notwendigen Tools rund um das Bereichsmanagement, damit Sie den Aufteilungsprozess verwalten können. Sie können auch genau steuern, wann und wie Regionsaufteilungen über eine RegionSplitPolicy erfolgen.

Die Anzahl der Regionen in einer Tabelle und die Aufteilung dieser Regionen sind entscheidende Faktoren für das Verständnis und die Optimierung Ihrer HBase-Clusterlast. Wenn Sie Ihre Schlüsselverteilung abschätzen können, sollten Sie die Tabelle mit Pre-Splitting erstellen, um die optimale anfängliche Ladeleistung zu erhalten. Sie können mit einem niedrigeren Vielfachen der Anzahl von Regionsservern als Ausgangspunkt für die anfängliche Anzahl von Regionen beginnen und die automatische Aufteilung übernehmen lassen. Wenn Sie die anfänglichen Aufteilungspunkte nicht richtig schätzen können, ist es besser, die Tabelle nur mit einer Region zu erstellen und ein anfängliches Laden mit automatischer Aufteilung zu starten und IncreasingToUpperBoundRegionSplitPolicy zu verwenden. Beachten Sie jedoch, dass sich die Gesamtzahl der Regionen im Laufe der Zeit stabilisieren wird und der aktuelle Satz von Regionsaufteilungspunkten aus den Daten bestimmt wird, die die Tabelle bisher erhalten hat. Möglicherweise möchten Sie die Lastverteilung auf die Regionen jederzeit überwachen, und wenn sich die Lastverteilung im Laufe der Zeit ändert, verwenden Sie die manuelle Aufteilung oder legen Sie aggressivere Regionenaufteilungsgrößen fest. Zu guter Letzt können Sie die kommende Online-Merge-Funktion ausprobieren und Ihren Anwendungsfall beisteuern.