Theorie und Praxis der Nutzung von HBase

Guten Tag! Mein Name ist Danil Lipovoy, unser Team bei Sbertech hat begonnen, HBase als Speicher für operationale Daten zu verwenden. Im Laufe der Untersuchung haben wir Erfahrungen gesammelt, die wir systematisieren und beschreiben wollten (wir hoffen, dass viele davon profitieren werden). Alle nachfolgend aufgeführten Experimente wurden mit den Versionen HBase 1.2.0-cdh5.14.2 und 2.0.0-cdh6.0.0-beta1 durchgeführt.

  1. Allgemeine Architektur
  2. Datenaufnahme in HBASE
  3. Datenlesung aus HBASE
  4. Daten-Caching
  5. Batch-Verarbeitung von Daten MultiGet/MultiPut
  6. Strategie zur Aufteilung von Tabellen in Regionen (Sharding)
  7. Fehlertoleranz, Kompaktion und Datenlokalität
  8. Einstellungen und Leistung
  9. Lasttest
  10. Das DBMS Tarantool ist ein attraktives, zukunftsträchtiges Produkt zur Erstellung von hochbelasteten Anwendungen.

1. Allgemeine Architektur

Theorie und Praxis der Nutzung von HBase
Der Backup-Master hört das Heartbeat des aktiven Masters auf dem ZooKeeper-Knoten und übernimmt im Falle eines Ausfalls die Funktionen des Masters.

2. Datenaufnahme in HBASE

Zunächst betrachten wir den einfachsten Fall – das Hinzufügen eines Schlüssel-Wert-Paares in eine Tabelle mit put(rowkey). Der Client muss zuerst herausfinden, wo sich der Root Region Server (Root Region Server — RRS) befindet, der die Tabelle hbase:meta speichert. Diese Information erhält er von ZooKeeper. Danach wendet er sich an den RRS und liest die Tabelle hbase:meta, aus der er die Information entnimmt, welcher RegionServer (RS) für die Speicherung der Daten für den angegebenen Schlüssel rowkey in der gewünschten Tabelle verantwortlich ist. Für die weitere Verwendung wird die Metadaten-Tabelle im Client zwischengespeichert, sodass nachfolgende Anfragen schneller direkt zum RS gehen.

Nachdem der RS die Anfrage erhalten hat, wird sie zuerst im WriteAheadLog (WAL) aufgezeichnet, was für die Wiederherstellung im Falle eines Fehlers notwendig ist. Dann speichert er die Daten im MemStore. Dies ist ein Puffer im Speicher, der eine sortierte Menge von Schlüsseln dieser Region enthält. Die Tabelle kann in Regionen (Partitionen) unterteilt werden, von denen jede eine nicht überlappende Menge von Schlüsseln enthält. Das ermöglicht, die Regionen auf verschiedenen Servern zu platzieren, um eine höhere Leistung zu erzielen. Dennoch, trotz der Offensichtlichkeit dieser Aussage, werden wir sehen, dass dies nicht in allen Fällen funktioniert.

Nach der Ablage des Eintrags im MemStore erhält der Client eine Rückmeldung, dass der Eintrag erfolgreich gespeichert wurde. Tatsächlich wird er jedoch nur im Puffer gehalten und gelangt erst nach einem bestimmten Zeitintervall oder bei Auffüllung mit neuen Daten auf die Festplatte.

Theorie und Praxis der Nutzung von HBase
Bei der Durchführung der Operation «Delete» erfolgt keine physische Löschung der Daten. Sie werden lediglich als gelöscht markiert, und die tatsächliche Zerstörung erfolgt zum Zeitpunkt des Aufrufs der Funktion major compact, die im Detail in Punkt 7 beschrieben wird.

Dateien im HFile-Format sammeln sich im HDFS an, und von Zeit zu Zeit wird der Prozess minor compact gestartet, der einfach kleine Dateien zu größeren zusammenfügt, ohne etwas zu löschen. Dies wird im Laufe der Zeit zu einem Problem, das sich nur beim Lesen von Daten zeigt (darauf kommen wir später zurück).

Neben dem oben beschriebenen Ladeprozess gibt es ein viel effizienteres Verfahren, das gewissermaßen die größte Stärke dieser Datenbank darstellt – BulkLoad. Dabei erstellen wir selbst HFiles und legen sie auf die Festplatte, was eine hervorragende Skalierbarkeit ermöglicht und ziemlich anständige Geschwindigkeiten erreicht. Im Grunde genommen ist die Grenze hier nicht HBase, sondern die Möglichkeiten der Hardware. Im Folgenden sind die Ladeergebnisse in einem Cluster aufgeführt, das aus 16 RegionServers und 16 NodeManagers YARN (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 Threads) besteht, Version HBase 1.2.0-cdh5.14.2.

Theorie und Praxis der Nutzung von HBase

Hier ist zu sehen, dass mit zunehmender Anzahl der Partitionen (Regionen) in der Tabelle sowie der Spark-Executor die Ladegeschwindigkeit zunimmt. Die Geschwindigkeit hängt auch vom Umfang der Schreibvorgänge ab. Große Blöcke führen zu einer Zunahme in MB/Sekunde, kleine in der Anzahl der pro Zeiteinheit eingefügten Datensätze, bei gleichen Bedingungen.

Es ist auch möglich, die Ladeprozesse in zwei Tabellen gleichzeitig zu starten und somit die Geschwindigkeit zu verdoppeln. Unten ist zu sehen, dass das gleichzeitige Schreiben von 10 KB-Blöcken in zwei Tabellen mit etwa 600 MB/Sekunde in jede Tabelle erfolgt (insgesamt 1275 MB/Sekunde), was mit der Schreibgeschwindigkeit in eine Tabelle von 623 MB/Sekunde übereinstimmt (siehe Nr. 11 oben).

Theorie und Praxis der Nutzung von HBase
Der zweite Versuch mit Aufzeichnungen von 50 KB zeigt jedoch, dass die Ladegeschwindigkeit nur noch geringfügig steigt, was auf eine Annäherung an die Grenzwerte hinweist. Dabei ist zu beachten, dass die Belastung für HBASE hier praktisch nicht entsteht; alles, was von ihm benötigt wird, ist zunächst die Übermittlung der Daten aus hbase:meta, und nach der Ablage der HFiles die Daten im BlockCache zu leeren und den MemStore-Puffer auf die Festplatte zu speichern, falls er nicht leer ist.

3. Lesen von Daten aus HBASE

Wenn man davon ausgeht, dass der gesamte Inhalt aus hbase:meta bereits beim Kunden vorliegt (siehe Punkt 2), dann geht die Anfrage sofort an denjenigen RS, der den benötigten Schlüssel speichert. Zuerst erfolgt die Suche in MemCache. Unabhängig davon, ob dort Daten vorhanden sind oder nicht, wird die Suche auch im BlockCache durchgeführt und, falls nötig, in HFiles. Wenn die Daten in der Datei gefunden werden, werden sie im BlockCache abgelegt und bei der nächsten Anfrage schneller zurückgegeben. Die Suche in HFile erfolgt relativ schnell dank der Verwendung des Bloom-Filters, d.h. durch das Einlesen eines kleinen Datenvolumens kann sofort festgestellt werden, ob diese Datei den benötigten Schlüssel enthält, und wenn nicht, wird zur nächsten übergegangen.

Theorie und Praxis der Nutzung von HBase
Nachdem die Daten aus diesen drei Quellen abgerufen wurden, erstellt der RS eine Antwort. Insbesondere kann er mehrere gefundene Versionen des Objekts übermitteln, wenn der Kunde Versionshistorie angefordert hat.

4. Daten-Caching

Die Puffer MemStore und BlockCache nehmen bis zu 80 % des zugewiesenen On-Heap-Speichers des RS ein (der Rest ist für die Dienstleistungsaufgaben des RS reserviert). Wenn der typische Nutzungsmodus so ist, dass Prozesse diese Daten schreiben und sofort wieder lesen, macht es Sinn, BlockCache zu reduzieren und MemStore zu erhöhen, da bei der Speicherung die Daten nicht in den Lesecache gelangen. Die Nutzung des BlockCaches würde seltener erfolgen. Der BlockCache-Puffer besteht aus zwei Teilen: LruBlockCache (immer On-Heap) und BucketCache (in der Regel Off-Heap oder auf SSD). BucketCache sollte verwendet werden, wenn es viele Leseanfragen gibt, die nicht in den LruBlockCache passen, was zu einer aktiven Arbeit des Garbage Collectors führt. Dabei ist mit einer radikalen Leistungssteigerung durch die Nutzung des Lesecaches nicht zu rechnen, jedoch werden wir in Punkt 8 noch darauf zurückkommen.

Theorie und Praxis der Nutzung von HBase
Der BlockCache ist für den gesamten RS einheitlich, während MemStore für jede Tabelle spezifisch ist (jeweils eines für jede Column Family).

Wie beschrieben in der Theorie gelangen Daten beim Schreiben nicht in den Cache, und tatsächlich sind solche Parameter wie CACHE_DATA_ON_WRITE für die Tabelle und "Cache DATA on Write" für den RS auf false gesetzt. In der Praxis jedoch, wenn Daten in MemStore geschrieben, dann auf die Festplatte abgelegt werden (was ihn somit leert), und wenn dann die resultierende Datei gelöscht wird, erhalten wir beim Ausführen einer get-Anfrage erfolgreich die Daten. Selbst wenn wir den BlockCache vollständig deaktivieren und die Tabelle mit neuen Daten füllen, dann MemStore auf die Festplatte zurücksetzen, diese löschen und aus einer anderen Sitzung anfragen, werden die Daten dennoch irgendwoher abgerufen. Somit speichert HBase nicht nur Daten, sondern auch mysteriöse Rätsel.

hbase(main):001:0> create 'ns:magic', 'cf'
Tabelle ns:magic erstellt
Dauer: 1.1533 Sekunden
hbase(main):002:0> put 'ns:magic', 'key1', 'cf:c', 'versuche_mich_zu_löschen'
Dauer: 0.2610 Sekunden
hbase(main):003:0> flush 'ns:magic'
Dauer: 0.6161 Sekunden
hdfs dfs -mv /data/hbase/data/ns/magic/* /tmp/trash
hbase(main):002:0> get 'ns:magic', 'key1'
 cf:c      timestamp=1534440690218, value=versuche_mich_zu_löschen

Der Parameter „Cache DATA on Read“ ist auf false gesetzt. Wenn Sie Ideen haben, sind Sie herzlich eingeladen, dies in den Kommentaren zu diskutieren.

5. Batch-Datenverarbeitung MultiGet/MultiPut

Die Verarbeitung einzelner Anfragen (Get/Put/Delete) ist eine recht kostenintensive Operation, daher sollten diese nach Möglichkeit in eine Liste zusammengefasst werden, was eine signifikante Leistungssteigerung ermöglicht. Dies gilt insbesondere für Schreiboperationen, während beim Lesen ein gewisses Problem auftritt. Im Diagramm unten ist die Lesezeit von 50.000 Datensätzen aus MemStore dargestellt. Das Lesen erfolgte in einem einzigen Thread, und auf der horizontalen Achse ist die Anzahl der Schlüssel in der Anfrage angegeben. Hier ist zu sehen, dass die Ausführungszeit bei einer Erhöhung auf bis zu tausend Schlüssel in einer Anfrage sinkt, d.h. die Geschwindigkeit steigt. Bei aktivem MSLAB-Modus beginnt jedoch nach diesem Schwellenwert ein drastischer Leistungsabfall, wobei die Arbeitszeit mit dem Datenvolumen im Datensatz steigt.

Theorie und Praxis der Nutzung von HBase

Die Tests wurden auf einer virtuellen Maschine mit 8 Kernen, Version HBase 2.0.0-cdh6.0.0-beta1, durchgeführt.

Der MSLAB-Modus soll die Fragmentierung des Heaps reduzieren, die durch die Vermischung neuer und alter Datengenerationen entsteht. Um das Problem zu lösen, werden die Daten beim Aktivieren von MSLAB in relativ kleine Zellen (Chunks) platziert und in Portionen verarbeitet. Das Ergebnis ist, dass die Leistung drastisch abnimmt, wenn das Volumen im angeforderten Datenpaket die zugewiesene Größe überschreitet. Andererseits ist das Deaktivieren dieses Modus ebenfalls unerwünscht, da dies zu Pausen aufgrund von GC während intensiver Datenverarbeitung führt. Eine gute Lösung besteht darin, die Chunk-Größe zu erhöhen, wenn gleichzeitig durch put geschrieben wird und gelesen wird. Es ist zu beachten, dass das Problem nicht auftritt, wenn nach dem Schreiben der Befehl flush ausgeführt wird, der MemStore auf die Festplatte schreibt, oder wenn der Ladevorgang mit BulkLoad erfolgt. In der folgenden Tabelle ist zu sehen, dass Abfragen aus MemStore mit größerem Datenvolumen (und gleicher Anzahl) zu einer Verlangsamung führen. Durch Erhöhung der Chunk-Größe kann die Verarbeitungszeit jedoch wieder auf den Normalwert zurückgeführt werden.

Theorie und Praxis der Nutzung von HBase
Neben der Erhöhung der chunksize hilft die Datenaufteilung nach Regionen, d.h. das Splitten von Tabellen. Dies führt dazu, dass weniger Anfragen pro Region eingehen und wenn diese in einer Zelle untergebracht sind, bleibt die Antwort gut.

6. Strategie zur Aufteilung von Tabellen in Regionen (Splitting)

Da HBase ein Key-Value-Speicher ist und die Partitionierung nach Schlüssel erfolgt, ist es äußerst wichtig, die Daten gleichmäßig auf alle Regionen zu verteilen. Beispielsweise führt die Partitionierung einer solchen Tabelle in drei Teile dazu, dass die Daten auf drei Regionen aufgeteilt werden:

Theorie und Praxis der Nutzung von HBase
Es kann vorkommen, dass dies zu einer erheblichen Verlangsamung führt, wenn die später hochgeladenen Daten überwiegend lange Werte enthalten, die alle mit derselben Ziffer beginnen, z.B.:

1000001
1000002

1100003

Da die Schlüssel als Byte-Array gespeichert werden, beginnen alle gleich und gehören zu einer Region #1, die diesen Schlüsselbereich speichert. Es gibt mehrere Aufteilungsstrategien:

HexStringSplit – Wandelt den Schlüssel in einen String mit hexadezimaler Kodierung im Bereich „00000000“ => „FFFFFFFF“ um, und füllt ihn links mit Nullen auf.

UniformSplit – Wandelt den Schlüssel in ein Byte-Array mit hexadezimaler Kodierung im Bereich „00“ => „FF“ um und füllt es rechts mit Nullen auf.

Außerdem kann ein beliebiger Bereich oder Satz von Schlüsseln für die Aufteilung angegeben und das Autospilitting konfiguriert werden. Eine der einfachsten und effektivsten Methoden ist jedoch UniformSplit und die Verwendung der Verkettung des Hashes, z.B. des vorderen Bytes des Schlüssels, der durch die Funktion CRC32(rowkey) gejagt wird, und des eigentlichen rowkey:

hash + rowkey

Dann werden alle Daten gleichmäßig auf die Regionen verteilt. Beim Lesen werden die ersten beiden Bytes einfach verworfen und der ursprüngliche Schlüssel bleibt erhalten. Außerdem überwacht der RS die Menge der Daten und Schlüssel in der Region und teilt sie automatisch in Teile auf, wenn die Grenzen überschritten werden.

7. Fehlertoleranz und Datenspeicherlokalität

Da jeder Satz von Schlüsseln nur für eine Region verantwortlich ist, besteht die Lösung für Probleme, die mit RS-Abstürzen oder der Stilllegung verbunden sind, darin, alle erforderlichen Daten in HDFS zu speichern. Bei einem Absturz entdeckt der Master dies durch den fehlenden Heartbeat auf dem ZooKeeper-Knoten. Er weist den verwalteten Region einem anderen RS zu, und da die HFiles in einem verteilten Dateisystem gespeichert sind, liest der neue Besitzer sie ein und setzt die Datenbereitstellung fort. Da jedoch ein Teil der Daten möglicherweise im MemStore vorhanden ist und es nicht rechtzeitig zu den HFiles geschafft hat, wird zum Wiederherstellen der Betriebsabläufe WAL verwendet, die ebenfalls in HDFS gespeichert werden. Nach der Übertragung der Änderungen kann der RS auf Anfragen reagieren, jedoch führt der Umzug dazu, dass ein Teil der Daten und die zur Bereitstellung dieser Daten dienenden Prozesse auf verschiedenen Knoten liegen, was die Lokalisierung verringert.

Die Lösung des Problems besteht in der Major Compaction – dieses Verfahren verschiebt Dateien auf die Knoten, die dafür verantwortlich sind (dort, wo sich ihre Regionen befinden), wodurch während dieses Verfahrens die Belastung des Netzwerks und der Festplatten erheblich steigt. In der Folge wird jedoch der Zugriff auf die Daten deutlich beschleunigt. Darüber hinaus führt die Major Compaction die Zusammenführung aller HFiles in eine Datei innerhalb der Region durch und bereinigt die Daten gemäß den Tabelleneinstellungen. Beispielsweise kann die Anzahl der Versionen eines Objekts festgelegt werden, die beibehalten werden sollen, oder die Lebensdauer des Objekts, nach deren Ablauf es physisch gelöscht wird.

Dieses Verfahren kann sich äußerst positiv auf die Leistung von HBase auswirken. Im Bild unten ist zu sehen, wie die Leistung aufgrund aktiver Datenschreibvorgänge abgenommen hat. Hier ist ersichtlich, wie in eine Tabelle 40 Threads schrieben und 40 Threads gleichzeitig Daten lasen. Die schreibenden Threads erzeugen immer mehr HFiles, die von anderen Threads eingelesen werden. In der Folge müssen immer mehr Daten aus dem Speicher gelöscht werden, und schließlich beginnt der GC zu arbeiten, der die gesamte Arbeit praktisch lähmt. Der Start der Major Compaction führte zur Bereinigung der entstandenen Stauungen und zur Wiederherstellung der Leistung.

Theorie und Praxis der Nutzung von HBase
Der Test wurde auf 3 DataNodes und 4 RS (CPU Xeon E5-2680 v4 @ 2,40 GHz * 64 Threads) durchgeführt. HBase-Version 1.2.0-cdh5.14.2

Es ist zu beachten, dass der Start der Major Compaction auf einer „lebenden“ Tabelle durchgeführt wurde, in die aktiv Daten geschrieben und gelesen wurden. Es wurde in der Community behauptet, dass dies zu inkorrekten Antworten beim Lesen von Daten führen kann. Zur Überprüfung wurde ein Prozess gestartet, der neue Daten generierte und sie in die Tabelle schrieb. Anschließend wurde sofort gelesen und überprüft, ob der erhaltene Wert mit dem übereinstimmt, was geschrieben wurde. Während dieses Prozesses wurde etwa 200 Mal eine Major Compaction durchgeführt und es wurden keine Fehler festgestellt. Möglicherweise tritt das Problem selten auf und nur bei hoher Auslastung, weshalb es sicherer ist, die Schreib- und Lesevorgänge planmäßig zu stoppen und die Bereinigung ohne solche GC-Drops durchzuführen.

Die Major Compaction hat außerdem keinen Einfluss auf den Zustand des MemStore. Um ihn auf die Festplatte zu schreiben und zu kompaktifizieren, muss flush verwendet werden (connection.getAdmin().flush(TableName.valueOf(tblName))).

8. Einstellungen und Leistung

Wie bereits erwähnt, zeigt HBase den größten Erfolg dort, wo es nichts tun muss, bei der Durchführung von BulkLoad. Dies gilt jedoch für die meisten Systeme und Menschen. Dieses Tool ist eher für das massenhafte Einfüllen von Daten in großen Blöcken geeignet, während, wenn der Prozess viele konkurrierende Lese- und Schreibanfragen erfordert, die oben beschriebenen Get- und Put-Befehle verwendet werden. Zur Bestimmung der optimalen Parameter wurden Testläufe mit verschiedenen Kombinationen von Tabelleneinstellungen und Parametern durchgeführt:

  • Es wurden 10 Threads gleichzeitig 3 Mal hintereinander gestartet (nennen wir dies einen Block von Threads).
  • Die Laufzeit aller Threads im Block wurde gemittelt und war das Endergebnis der Blockausführung.
  • Alle Threads arbeiteten mit derselben Tabelle.
  • Vor jedem Start des Blockes von Threads wurde eine Major Compaction durchgeführt.
  • Jeder Block führte nur eine der folgenden Operationen aus:

— Put
— Get
— Get+Put

  • Jeder Block führte 50.000 Wiederholungen seiner Operation aus.
  • Die Größe des Schreibvorgangs im Block betrug 100 Byte, 1000 Byte oder 10.000 Byte (random).
  • Blöcke wurden mit unterschiedlicher Anzahl an angeforderten Schlüsseln gestartet (entweder ein Schlüssel oder 10).
  • Blöcke wurden mit verschiedenen Tabelleneinstellungen gestartet. Die Parameter wurden geändert:

— BlockCache = ein- oder ausgeschaltet
— BlockSize = 65 KB oder 16 KB
— Partitionen = 1, 5 oder 30
— MSLAB = ein- oder ausgeschaltet

So sieht ein Block aus:

a. Der MSLAB-Modus wurde ein- oder ausgeschaltet.
b. Es wurde eine Tabelle erstellt, für die folgende Parameter festgelegt wurden: BlockCache = true/none, BlockSize = 65/16 Kb, Partitionen = 1/5/30.
c. Es wurde GZ-Kompression aktiviert.
d. Es wurden 10 Threads gleichzeitig gestartet, die jeweils 1/10 Operationen put/get/get+put in diese Tabelle mit Einträgen von 100/1000/10000 Byte ausführten, wobei 50.000 Anfragen hintereinander durchgeführt wurden (Schlüssel sind zufällig).
e. Punkt d wurde dreimal wiederholt.
f. Die Laufzeit aller Threads wurde gemittelt.

Alle möglichen Kombinationen wurden überprüft. Vorhersehbar ist, dass bei steigendem Datensatz die Geschwindigkeit sinken wird oder dass die Deaktivierung des Cachings zu einer Verlangsamung führt. Das Ziel war es jedoch, das Ausmaß und die Bedeutung des Einflusses jedes Parameters zu verstehen, daher wurden die gesammelten Daten in die Funktion der linearen Regression eingegeben, die es ermöglicht, die Zuverlässigkeit mit Hilfe der t-Statistik zu bewerten. Im Folgenden sind die Ergebnisse der Blöcke aufgeführt, die die Put-Operationen durchführen. Der vollständige Kombinationssatz beträgt 2*2*3*2*3 = 144 Varianten + 72, da einige zweimal durchgeführt wurden. Insgesamt also 216 Durchläufe:

Theorie und Praxis der Nutzung von HBase
Die Tests wurden auf einem Mini-Cluster durchgeführt, das aus 3 DataNodes und 4 RS besteht (CPU Xeon E5-2680 v4 @ 2,40 GHz * 64 Threads). Version HBase 1.2.0-cdh5.14.2.

Die höchste Einfügegeschwindigkeit von 3,7 Sekunden wurde bei deaktiviertem MSLAB-Modus auf einer Tabelle mit einer Partition, mit aktiviertem BlockCache, BlockSize = 16, Einträgen von 100 Byte in 10er-Paketen erreicht.
Die niedrigste Einfügegeschwindigkeit von 82,8 Sekunden wurde bei aktiviertem MSLAB-Modus auf einer Tabelle mit einer Partition, mit aktiviertem BlockCache, BlockSize = 16, Einträgen von 10000 Byte einzeln erreicht.

Schauen wir uns jetzt das Modell an. Wir sehen eine gute Modellqualität anhand von R2, aber es ist offensichtlich, dass Extrapolation hier nicht empfehlenswert ist. Das tatsächliche Verhalten des Systems bei Änderungen der Parameter wird nicht linear sein, dieses Modell ist nicht für Prognosen gedacht, sondern für das Verständnis dessen, was im Rahmen der festgelegten Parameter passiert ist. Zum Beispiel sehen wir hier nach dem Student-Kriterium, dass die Parameter BlockSize und BlockCache für die Put-Operation keine Rolle spielen (was im Allgemeinen ganz vorhersehbar ist):

Theorie und Praxis der Nutzung von HBase
Es ist jedoch etwas überraschend, dass die Erhöhung der Anzahl der Partitionen zu einer Verringerung der Leistung führt (wir haben bereits positive Auswirkungen einer erhöhten Anzahl von Partitionen bei BulkLoad gesehen), obwohl es nachvollziehbar ist. Erstens müssen für die Verarbeitung Anfragen an 30 Regionen anstelle von einer gestellt werden, und das Datenvolumen ist nicht so groß, dass dies einen Vorteil bringt. Zweitens wird die Gesamtlaufzeit durch den langsamsten RS bestimmt, und da die Anzahl der DataNodes geringer ist als die der RS, haben einige Regionen eine null Lokalisierung. Schauen wir uns schließlich die fünf Hauptakteure an:

Theorie und Praxis der Nutzung von HBase
Lassen Sie uns nun die Ergebnisse der Ausführung der Get-Blocks bewerten:

Theorie und Praxis der Nutzung von HBase
Die Anzahl der Partitionen hat an Bedeutung verloren, was wahrscheinlich darauf zurückzuführen ist, dass die Daten gut im Cache gespeichert werden und der Cache für das Lesen das bedeutendste (statistisch gesehen) Kriterium ist. Es ist selbstverständlich, dass eine Erhöhung der Anzahl der Nachrichten in der Anfrage ebenfalls sehr vorteilhaft für die Leistung ist. Die besten Ergebnisse:

Theorie und Praxis der Nutzung von HBase
Schließlich schauen wir uns das Blockmodell an, das zunächst get und dann put ausgeführt hat:

Theorie und Praxis der Nutzung von HBase
Hier sind alle Parameter signifikant. Und die Ergebnisse der Führenden:

Theorie und Praxis der Nutzung von HBase

9. Lasttests

Und schließlich führen wir eine mehr oder weniger angemessene Last aus, aber es ist immer interessanter, wenn man einen Vergleich hat. Auf der DataStax-Website – dem Schlüsselentwickler von Cassandra – gibt es Ergebnisse eine Anzahl von NoSQL-Speichern, einschließlich HBase Version 0.98.6-1. Die Belastung wurde mit 40 Threads und einer Datenmenge von 100 Bytes auf SSD-Festplatten durchgeführt. Die Testergebnisse der Read-Modify-Write-Operationen zeigten solche Resultate.

Theorie und Praxis der Nutzung von HBase
Soweit ich verstanden habe, wurde das Lesen in Blöcken von 100 Datensätzen durchgeführt, und für 16 HBase-Knoten zeigte der DataStax-Test eine Leistung von 10.000 Operationen pro Sekunde.

Es ist günstig, dass wir auch 16 Knoten in unserem Cluster haben, aber nicht ganz so „günstig“, dass jeder 64 Kerne (Threads) besitzt, während im DataStax-Test nur 4 vorhanden sind. Andererseits verfügen sie über SSDs, während wir HDDs haben, und eine neuere Version von HBase, und die CPU-Auslastung während der Belastung nahm kaum signifikant zu (visuell um 5-10 Prozent). Trotzdem werden wir versuchen, mit dieser Konfiguration zu starten. Die Tabelleneinstellungen sind standardmäßig, das Lesen erfolgt zufällig im Schlüsselbereich von 0 bis 50 Millionen (d.h. im Grunde jedes Mal neu). Die Tabelle hat 50 Millionen Einträge, die auf 64 Partitionen aufgeteilt sind. Die Schlüssel sind nach crc32 gehasht. Die Tabelleneinstellungen sind standardmäßig, MSLAB ist aktiviert. Es werden 40 Threads gestartet, jeder Thread liest einen Satz von 100 zufälligen Schlüsseln und schreibt gleich darauf 100 generierte Bytes für diese Schlüssel zurück.

Theorie und Praxis der Nutzung von HBase
Stand: 16 DataNode und 16 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 Threads). HBase-Version 1.2.0-cdh5.14.2.

Das durchschnittliche Ergebnis liegt näher bei 40.000 Operationen pro Sekunde, was deutlich besser ist als im DataStax-Test. Allerdings kann man im Rahmen des Experiments die Bedingungen etwas ändern. Es ist ziemlich unwahrscheinlich, dass die gesamte Arbeit ausschließlich mit einer Tabelle und nur mit eindeutigen Schlüsseln durchgeführt wird. Angenommen, es gibt einen „heißen“ Satz von Schlüsseln, der die Hauptlast erzeugt. Daher versuchen wir, die Last mit größeren Einträgen (10 KB) zu erzeugen, ebenfalls in Mengen von 100, in 4 verschiedenen Tabellen und die angeforderten Schlüsselfelder auf 50.000 einzuschränken. Im folgenden Diagramm wird der Start von 40 Threads angezeigt, jeder Thread liest einen Satz von 100 Schlüsseln und schreibt zufällige 10 KB für diese Schlüssel zurück.

Theorie und Praxis der Nutzung von HBase
Stand: 16 DataNode und 16 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 Threads). HBase-Version 1.2.0-cdh5.14.2.

Im Verlauf der Belastung wurde mehrmals eine Major Compaction gestartet. Wie oben gezeigt, wird die Leistung ohne dieses Verfahren allmählich abnehmen, jedoch tritt während der Ausführung auch zusätzliche Belastung auf. Die Einbrüche werden durch verschiedene Ursachen verursacht. Manchmal beendeten Threads ihre Arbeit, und während sie neu gestartet wurden, trat eine Pause auf; manchmal erzeugten externe Anwendungen Belastung für den Cluster.

Das gleichzeitige Lesen und Schreiben ist eines der schwierigsten Arbeitsszenarien für HBase. Wenn nur Put-Anfragen kleiner Größe, zum Beispiel 100 Byte, durchgeführt werden und diese zu Paketen von 10-50 Tausend zusammengefasst werden, können Hunderte von Tausenden von Operationen pro Sekunde erreicht werden, und ähnlich verhält es sich mit reinen Leseanfragen. Es ist erwähnenswert, dass die Ergebnisse radikal besser sind als die von DataStax, hauptsächlich dank der Blockanfragen von 50.000.

Theorie und Praxis der Nutzung von HBase
Stand: 16 DataNode und 16 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 Threads). HBase-Version 1.2.0-cdh5.14.2.

10. Fazit

Dieses System ist ausreichend flexibel konfigurierbar, dennoch bleibt der Einfluss einer großen Anzahl von Parametern nach wie vor unbekannt. Ein Teil von ihnen wurde getestet, hat aber nicht in das endgültige Testset Eingang gefunden. Erste Experimente zeigten eine geringe Bedeutung des Parameters DATA_BLOCK_ENCODING, der Informationen unter Verwendung von Werten benachbarter Zellen kodiert, was für zufällig generierte Daten durchaus erklärbar ist. Bei der Verwendung einer großen Anzahl von sich wiederholenden Objekten kann der Gewinn erheblich sein. Insgesamt kann gesagt werden, dass HBase den Eindruck einer ernsthaften und durchdachten Datenbank vermittelt, die bei Operationen mit großen Datenblöcken ausreichend leistungsfähig sein kann. Besonders wenn es möglich ist, Lese- und Schreibvorgänge zeitlich zu entzerren.

Wenn etwas Ihrer Meinung nach nicht ausreichend erläutert ist, bin ich bereit, mehr Details zu geben. Ich lade ein, unsere Erfahrungen zu teilen oder zu diskutieren, wenn Sie mit etwas nicht einverstanden sind.

Quelle: habr.com

60GB SSD 8Gb DDR4