Bonjour ! Je m'appelle Danil Lipovoy, notre équipe chez Sbertech a commencé à utiliser HBase comme stockage de données opérationnelles. Au cours de son étude, nous avons accumulé de l'expérience que nous souhaitions systématiser et décrire (nous espérons que cela sera utile à beaucoup). Toutes les expériences ci-dessous ont été réalisées avec les versions HBase 1.2.0-cdh5.14.2 et 2.0.0-cdh6.0.0-beta1.
- Architecture générale
- Ăcriture de donnĂ©es dans HBASE
- Lecture de données depuis HBASE
- Mise en cache des données
- Traitement par lots des données MultiGet / MultiPut
- Stratégie de partitionnement des tables en régions (sharding)
- Tolérance aux pannes, compactage et localité des données
- Configurations et performances
- Test de charge
- Conclusions
1. Architecture générale

Un Master de secours Ă©coute le heartbeat du maĂźtre actif sur le nĆud ZooKeeper et, en cas de disparition, prend les fonctions du maĂźtre.
2. Ăcriture de donnĂ©es dans HBASE
Commençons par le cas le plus simple : Ă©crire un objet clĂ©-valeur dans une table Ă l'aide de put(rowkey). Le client doit d'abord dĂ©terminer oĂč se trouve le serveur de rĂ©gion racine (Root Region Server - RRS) qui stocke la table hbase:meta. Cette information est obtenue auprĂšs de ZooKeeper. Ensuite, il s'adresse au RRS et lit la table hbase:meta, d'oĂč il extrait l'information sur le RegionServer (RS) responsable du stockage des donnĂ©es selon la clĂ© rowkey donnĂ©e dans la table qui l'intĂ©resse. Pour une utilisation ultĂ©rieure, la mĂ©ta-table est mise en cache par le client et donc les accĂšs suivants se font plus rapidement, directement auprĂšs du RS.
Ensuite, le RS, ayant reçu la requĂȘte, Ă©crit d'abord celle-ci dans le WriteAheadLog (WAL), ce qui est nĂ©cessaire pour la rĂ©cupĂ©ration en cas de panne. Il enregistre ensuite les donnĂ©es dans le MemStore. C'est un tampon en mĂ©moire qui contient un ensemble de clĂ©s triĂ©es de cette rĂ©gion. La table peut ĂȘtre divisĂ©e en rĂ©gions (partitions), chacune d'elles contenant un ensemble de clĂ©s disjointes. Cela permet, en plaçant les rĂ©gions sur diffĂ©rents serveurs, d'obtenir des performances plus Ă©levĂ©es. Cependant, malgrĂ© l'Ă©vidence de cette affirmation, nous verrons par la suite que cela ne fonctionne pas dans tous les cas.
AprÚs avoir placé l'enregistrement dans le MemStore, le client reçoit une réponse indiquant que l'enregistrement a été enregistré avec succÚs. En réalité, il est uniquement stocké dans le tampon et ne sera écrit sur le disque qu'aprÚs un certain intervalle de temps ou lorsque celui-ci sera rempli de nouvelles données.

Lors de l'opération «Delete», il n'y a pas de suppression physique des données. Elles sont simplement marquées comme supprimées, et la destruction effective se produit lors de l'appel à la fonction major compact, dont les détails sont donnés au point 7.
Les fichiers au format HFile s'accumulent dans HDFS et de temps en temps, un processus de minor compact est lancé, qui fusionne simplement les petits fichiers en de plus gros, sans rien supprimer. Au fil du temps, cela devient un problÚme qui se manifeste uniquement lors de la lecture des données (nous y reviendrons un peu plus tard).
En plus du processus de chargement dĂ©crit ci-dessus, il existe une procĂ©dure beaucoup plus efficace, qui constitue sans doute le principal atout de cette base de donnĂ©es â BulkLoad. Elle consiste Ă former nous-mĂȘmes des HFiles et Ă les dĂ©poser sur le disque, ce qui permet un excellent passage Ă l'Ă©chelle et d'atteindre des vitesses trĂšs satisfaisantes. En fait, la limite ici n'est pas HBase, mais les capacitĂ©s matĂ©rielles. Ci-dessous, vous trouverez les rĂ©sultats de chargement sur un cluster composĂ© de 16 RegionServers et de 16 NodeManagers YARN (CPU Xeon E5-2680 v4 @ 2,40 GHz * 64 threads), version HBase 1.2.0-cdh5.14.2.

Ici, on constate qu'en augmentant le nombre de partitions (régions) dans la table, ainsi que le nombre d'exécuteurs Spark, on obtient une augmentation de la vitesse de chargement. La vitesse dépend également du volume d'écriture. Les gros blocs donnent un gain en termes de Mo/s, tandis que les petits blocs augmentent le nombre d'enregistrements insérés par unité de temps, toutes choses égales par ailleurs.
Il est également possible de lancer le chargement dans deux tables simultanément et de doubler la vitesse. Ci-dessous, on peut voir que l'écriture de blocs de 10 Ko dans deux tables se fait à une vitesse d'environ 600 Mo/s chacune (soit un total de 1275 Mo/s), ce qui correspond à la vitesse d'écriture dans une seule table de 623 Mo/s (voir n°11 ci-dessus).

Lors du second lancement avec des écritures de 50 Ko, on constate que la vitesse de chargement n'augmente plus de maniÚre significative, ce qui indique que l'on approche des valeurs limites. Il convient également de noter qu'il n'y a pratiquement pas de charge sur HBASE ici, tout ce qui est requis de lui est d'abord de renvoyer les données de hbase:meta, puis aprÚs le dépÎt des HFiles, de vider les données du BlockCache et de sauvegarder le buffer MemStore sur le disque, s'il n'est pas vide.
3. Lecture des données depuis HBASE
Si l'on considĂšre que toutes les informations de hbase:meta sont dĂ©jĂ disponibles pour le client (voir point 2), la requĂȘte est envoyĂ©e immĂ©diatement au RS oĂč la clĂ© dĂ©sirĂ©e est stockĂ©e. Tout d'abord, la recherche s'effectue dans MemCache. Que des donnĂ©es y soient prĂ©sentes ou non, la recherche s'effectue Ă©galement dans le tampon BlockCache et, si nĂ©cessaire, dans HFiles. Si des donnĂ©es sont trouvĂ©es dans le fichier, elles sont placĂ©es dans BlockCache et seront retournĂ©es plus rapidement lors de la prochaine requĂȘte. La recherche dans HFile se fait relativement rapidement grĂące Ă l'utilisation du filtre de Bloom, c'est-Ă -dire qu'en lisant un petit volume de donnĂ©es, il dĂ©termine immĂ©diatement si ce fichier contient la clĂ© recherchĂ©e, et si ce n'est pas le cas, il passe au suivant.

AprÚs avoir obtenu des données de ces trois sources, le RS forme une réponse. En particulier, il peut transmettre plusieurs versions trouvées d'un objet si le client a demandé la gestion des versions.
4. Mise en cache des données
Les tampons MemStore et BlockCache occupent jusqu'Ă 80 % de la mĂ©moire on-heap allouĂ©e au RS (le reste Ă©tant rĂ©servĂ© aux tĂąches de service du RS). Si le mode d'utilisation typique est tel que les processus Ă©crivent et lisent immĂ©diatement ces mĂȘmes donnĂ©es, il est judicieux de rĂ©duire BlockCache et d'augmenter MemStore, puisque lors de l'Ă©criture, les donnĂ©es ne sont pas mises en cache pour la lecture, rendant l'utilisation de BlockCache moins frĂ©quente. Le tampon BlockCache se compose de deux parties : LruBlockCache (toujours on-heap) et BucketCache (gĂ©nĂ©ralement off-heap ou sur SSD). BucketCache doit ĂȘtre utilisĂ© lorsque les requĂȘtes de lecture sont trĂšs nombreuses et qu'elles ne tiennent pas dans LruBlockCache, ce qui conduit Ă un travail actif du Garbage Collector. Cependant, il ne faut pas attendre une augmentation radicale des performances grĂące Ă l'utilisation du cache de lecture, mais cela sera discutĂ© plus en dĂ©tail au point 8.

BlockCache est unique pour tout le RS, tandis que MemStore est spécifique à chaque table (un par Column Family).
Comment En thĂ©orie, lors de l'Ă©criture, les donnĂ©es ne vont pas dans le cache et en effet, les paramĂštres CACHE_DATA_ON_WRITE pour la table et « Cache DATA on Write » pour le RS sont dĂ©finis sur false. Cependant, en pratique, si l'on Ă©crit des donnĂ©es dans MemStore, puis que l'on les vide sur disque (effaçant ainsi ce qu'il y a), puis que l'on supprime le fichier rĂ©sultant, en effectuant la requĂȘte get, nous obtenons avec succĂšs les donnĂ©es. MĂȘme si l'on dĂ©sactive complĂštement BlockCache et que l'on remplit la table avec de nouvelles donnĂ©es, ensuite pour provoquer la vidange de MemStore sur disque, les supprimer et les requĂ©rir d'une autre session, elles seront quand mĂȘme extraites de quelque part. Ainsi, HBase conserve non seulement des donnĂ©es, mais aussi des mystĂšres mystĂ©rieux.
hbase(main):001:0> create 'ns:magic', 'cf'
Table ns:magic créée
Temps d'exécution 1.1533 secondes
hbase(main):002:0> put 'ns:magic', 'key1', 'cf:c', 'try_to_delete_me'
Temps d'exécution 0.2610 secondes
hbase(main):003:0> flush 'ns:magic'
Temps d'exécution 0.6161 secondes
hdfs dfs -mv /data/hbase/data/ns/magic/* /tmp/trash
hbase(main):002:0> get 'ns:magic', 'key1'
cf:c timestamp=1534440690218, value=try_to_delete_me
Le paramÚtre « Cache DATA on Read » est défini sur false. Si vous avez des idées, n'hésitez pas à en discuter dans les commentaires.
5. Traitement par lots de données MultiGet/MultiPut
Le traitement des requĂȘtes individuelles (Get/Put/Delete) est une opĂ©ration assez coĂ»teuse, il est donc recommandĂ© de les regrouper autant que possible dans une liste, ce qui permet d'obtenir un gain de performance significatif. Cela est particuliĂšrement vrai pour les opĂ©rations d'Ă©criture, tandis qu'il existe un inconvĂ©nient lors de la lecture. Le graphique ci-dessous montre le temps de lecture de 50 000 enregistrements depuis MemStore. La lecture a Ă©tĂ© effectuĂ©e en un seul thread et l'axe horizontal montre le nombre de clĂ©s dans la requĂȘte. Nous pouvons voir qu'en augmentant jusqu'Ă mille clĂ©s dans une requĂȘte, le temps d'exĂ©cution diminue, c'est-Ă -dire que la vitesse augmente. Cependant, en mode MSLAB par dĂ©faut, aprĂšs ce seuil, il y a une chute radicale des performances, et plus le volume de donnĂ©es dans l'enregistrement est important, plus le temps d'exĂ©cution augmente.

Les tests ont Ă©tĂ© rĂ©alisĂ©s sur une machine virtuelle, 8 cĆurs, version HBase 2.0.0-cdh6.0.0-beta1.
Le mode MSLAB vise Ă rĂ©duire la fragmentation du tas, qui se produit Ă cause du mĂ©lange des donnĂ©es de gĂ©nĂ©rations nouvelles et anciennes. Comme solution Ă ce problĂšme, lorsque MSLAB est activĂ©, les donnĂ©es sont placĂ©es dans des cellules relativement petites (chunks) et traitĂ©es par lots. En consĂ©quence, lorsque le volume du paquet de donnĂ©es demandĂ© dĂ©passe la taille allouĂ©e, les performances chutent rapidement. D'autre part, la dĂ©sactivation de ce mode n'est pas souhaitable non plus, car cela entraĂźnera des pauses dues au GC lors de moments de travail intensif avec les donnĂ©es. Une bonne solution consiste Ă augmenter les volumes des cellules, en cas d'Ă©criture active par put en mĂȘme temps que la lecture. Il convient de noter que le problĂšme ne se pose pas si, aprĂšs l'Ă©criture, nous exĂ©cutons la commande flush qui pousse MemStore sur le disque ou si un chargement est effectuĂ© par BulkLoad. Le tableau ci-dessous montre que les requĂȘtes provenant de MemStore de volumes de donnĂ©es plus importants (et d'un nombre identique) entraĂźnent un ralentissement. Cependant, en augmentant la taille des chunks, nous ramenons le temps de traitement Ă la normale.

En plus d'augmenter le chunksize, le partitionnement des donnĂ©es par rĂ©gions, c'est-Ă -dire le splitting des tables, aide. Cela signifie qu'un nombre rĂ©duit de requĂȘtes est envoyĂ© Ă chaque rĂ©gion, et si elles sont toutes placĂ©es dans une cellule, la rĂ©ponse reste bonne.
6. Stratégie de partitionnement des tables par régions (splitting)
Ătant donnĂ© qu'HBase est un stockage clĂ©-valeur et que le partitionnement s'effectue selon la clĂ©, il est essentiel de rĂ©partir les donnĂ©es de maniĂšre uniforme sur toutes les rĂ©gions. Par exemple, le partitionnement d'une telle table en trois parties aura pour rĂ©sultat que les donnĂ©es seront rĂ©parties sur trois rĂ©gions :

Il arrive que cela entraĂźne un ralentissement drastique si les donnĂ©es chargĂ©es par la suite se prĂ©sentent sous la forme de valeurs long, majoritairement commençant par un mĂȘme chiffre, par exemple :
1000001
1000002
âŠ
1100003
Comme les clĂ©s sont stockĂ©es sous forme de tableau d'octets, toutes commenceront de la mĂȘme maniĂšre et iront Ă la rĂ©gion #1 qui contient cette plage de clĂ©s. Il existe plusieurs stratĂ©gies de partitionnement :
HexStringSplit â Transforme la clĂ© en une chaĂźne avec un encodage hexadĂ©cimal dans la plage «00000000» â «FFFFFFFF», en remplissant avec des zĂ©ros Ă gauche.
UniformSplit â Transforme la clĂ© en un tableau d'octets avec un encodage hexadĂ©cimal dans la plage «00» â «FF», en remplissant avec des zĂ©ros Ă droite.
De plus, il est possible de spĂ©cifier n'importe quelle plage ou ensemble de clĂ©s pour le partitionnement et de configurer le splitting automatique. Cependant, l'une des approches les plus simples et efficaces est UniformSplit et l'utilisation de la concatĂ©nation de hachage, par exemple, la paire d'octets la plus significative provenant du passage de la clĂ© par la fonction CRC32(rowkey) et la clĂ© elle-mĂȘme :
hash + rowkey
Ainsi, toutes les données seront réparties uniformément sur les régions. Lors de la lecture, les deux premiers octets sont simplement ignorés, et la clé d'origine reste. De plus, le RS contrÎle le nombre de données et de clés dans une région et, si les limites sont dépassées, divise automatiquement celle-ci en parties.
7. Résilience et localité des données
Ătant donnĂ© qu'un seul rĂ©gion est responsable de chaque ensemble de clĂ©s, la solution aux problĂšmes liĂ©s aux pannes de RS ou Ă la mise hors service consiste Ă stocker toutes les donnĂ©es nĂ©cessaires dans HDFS. En cas de panne de RS, le maĂźtre le dĂ©tecte par l'absence de heartbeat sur le nĆud ZooKeeper. Il assigne alors la rĂ©gion Ă un autre RS, et comme les HFiles sont stockĂ©s dans un systĂšme de fichiers distribuĂ©, le nouveau propriĂ©taire les lit et continue Ă gĂ©rer les donnĂ©es. Cependant, comme certaines donnĂ©es peuvent ĂȘtre dans MemStore et n'ont pas encore Ă©tĂ© Ă©crites dans HFiles, l'historique des opĂ©rations est restaurĂ© Ă l'aide de WAL, qui sont Ă©galement stockĂ©s dans HDFS. AprĂšs l'application des modifications, le RS peut rĂ©pondre aux requĂȘtes, mais le transfert entraĂźne une sĂ©paration des donnĂ©es et des processus de gestion sur diffĂ©rents nĆuds, ce qui diminue la locality.
La solution au problĂšme est la compaction majeure â cette procĂ©dure dĂ©place les fichiers vers les nĆuds responsables (lĂ oĂč se trouvent leurs rĂ©gions), ce qui entraĂźne une augmentation brutale de la charge sur le rĂ©seau et les disques durant cette procĂ©dure. Cependant, par la suite, l'accĂšs aux donnĂ©es s'accĂ©lĂšre considĂ©rablement. De plus, la major_compaction fusionne tous les HFiles en un seul fichier au sein de la rĂ©gion, et nettoie les donnĂ©es en fonction des paramĂštres de la table. Par exemple, il est possible de dĂ©finir le nombre de versions d'objet Ă conserver ou la durĂ©e de vie aprĂšs laquelle l'objet est physiquement supprimĂ©.
Cette procĂ©dure peut avoir un impact trĂšs positif sur le fonctionnement d'HBase. L'image ci-dessous montre comment la performance a Ă©tĂ© dĂ©gradĂ©e en raison de l'Ă©criture active de donnĂ©es. On voit ici 40 threads Ă©crivant dans une table et 40 threads lisant des donnĂ©es simultanĂ©ment. Les threads d'Ă©criture gĂ©nĂšrent de plus en plus de HFiles, qui sont ensuite lus par d'autres threads. En consĂ©quence, de plus en plus de donnĂ©es doivent ĂȘtre supprimĂ©es de la mĂ©moire, et finalement, le GC commence Ă s'exĂ©cuter, paralysant pratiquement tout le processus. Le lancement de la compaction majeure a permis de nettoyer les encombrements gĂ©nĂ©rĂ©s et de restaurer la performance.

Le test a été réalisé sur 3 DataNodes et 4 RS (CPU Xeon E5-2680 v4 @ 2,40 GHz * 64 threads). Version HBase 1.2.0-cdh5.14.2
Il convient de noter que le lancement de la compaction majeure a Ă©tĂ© effectuĂ© sur une table "vivante", oĂč des donnĂ©es Ă©taient activement lues et Ă©crites. Il a Ă©tĂ© avancĂ© sur le rĂ©seau que cela pourrait entraĂźner une rĂ©ponse incorrecte lors de la lecture des donnĂ©es. Un processus a Ă©tĂ© lancĂ© pour gĂ©nĂ©rer de nouvelles donnĂ©es et les Ă©crire dans la table. Ensuite, il lisait immĂ©diatement et vĂ©rifiait si la valeur obtenue correspondait Ă celle qui avait Ă©tĂ© enregistrĂ©e. Pendant le fonctionnement de ce processus, la compaction majeure a Ă©tĂ© lancĂ©e environ 200 fois sans aucun Ă©chec enregistrĂ©. Il est possible que le problĂšme se manifeste rarement et uniquement lors de charges Ă©levĂ©es, il est donc prĂ©fĂ©rable d'arrĂȘter le processus d'Ă©criture et de lecture de maniĂšre planifiĂ©e et d'effectuer l'entretien sans permettre de telles baisses du GC.
De plus, la compaction majeure n'affecte pas l'état du MemStore, pour le vider sur disque et le compacter, il faut utiliser flush (connection.getAdmin().flush(TableName.valueOf(tblName))).
8. ParamĂštres et performance
Comme mentionnĂ© prĂ©cĂ©demment, HBase montre le plus de succĂšs lĂ oĂč il n'a rien Ă faire, lors de l'exĂ©cution du BulkLoad. Cela s'applique d'ailleurs Ă la plupart des systĂšmes et des personnes. Cependant, cet outil est plus adaptĂ© Ă l'insertion massive de donnĂ©es par gros blocs, alors que si le processus nĂ©cessite l'exĂ©cution de plusieurs requĂȘtes concurrentes de lecture et d'Ă©criture, les commandes Get et Put dĂ©crites ci-dessus sont utilisĂ©es. Afin de dĂ©terminer les paramĂštres optimaux, des exĂ©cutions ont Ă©tĂ© rĂ©alisĂ©es avec diffĂ©rentes combinaisons de paramĂštres de tables et de configurations :
- 10 threads ont été lancés simultanément 3 fois de suite (appelons cela un bloc de threads).
- Le temps de fonctionnement de tous les threads dans le bloc était moyen et constituait le résultat final du bloc.
- Tous les threads travaillaient avec la mĂȘme table.
- Avant chaque lancement du bloc de threads, une compaction majeure était effectuée.
- Chaque bloc ne réalisait qu'une des opérations suivantes :
â Put
â Get
â Get+Put
- Chaque bloc exécutait 50 000 répétitions de son opération.
- La taille d'enregistrement dans le bloc était de 100 octets, 1000 octets ou 10000 octets (aléatoire).
- Les blocs étaient lancés avec un nombre variable de clés demandées (soit une clé ou 10).
- Les blocs étaient lancés avec différentes configurations de table. Les paramÚtres ont été modifiés :
â BlockCache = activĂ© ou dĂ©sactivĂ©
â BlockSize = 65 Ko ou 16 Ko
â Partitions = 1, 5 ou 30
â MSLAB = activĂ© ou dĂ©sactivĂ©
Ainsi, le bloc se présente comme suit :
a. Le mode MSLAB était activé/désactivé.
b. Un tableau a été créé, pour lequel les paramÚtres suivants ont été définis : BlockCache = true/none, BlockSize = 65/16 Ko, Partitions = 1/5/30.
c. La compression GZ a été configurée.
d. 10 threads Ă©taient lancĂ©s simultanĂ©ment, effectuant 1/10 opĂ©rations put/get/get+put dans ce tableau avec des enregistrements de 100/1000/10000 octets, exĂ©cutant 50 000 requĂȘtes consĂ©cutives (clĂ©s alĂ©atoires).
e. Le point d a été répété trois fois.
f. Le temps de fonctionnement de tous les threads a été moyenné.
Toutes les combinaisons possibles ont été vérifiées. Il est prévisible qu'en augmentant la taille de l'enregistrement, la vitesse diminuera ou que la désactivation du cache entraßnera un ralentissement. Cependant, l'objectif était de comprendre l'ampleur et l'importance de l'influence de chaque paramÚtre, donc les données collectées ont été soumises à une fonction de régression linéaire, ce qui permet d'évaluer la fiabilité à l'aide de la statistique t. Ci-dessous sont les résultats des blocs exécutant des opérations Put. L'ensemble complet de combinaisons est de 2*2*3*2*3 = 144 variantes + 72 car certaines ont été exécutées deux fois. Ainsi, au total, il y a eu 216 exécutions :

Les tests ont été réalisés sur un mini-cluster composé de 3 DataNodes et 4 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 threads). Version HBase 1.2.0-cdh5.14.2.
La vitesse d'insertion la plus élevée de 3,7 secondes a été obtenue en mode MSLAB désactivé, sur une table avec une seule partition, avec BlockCache activé, BlockSize = 16, des enregistrements de 100 octets en paquet de 10.
La vitesse d'insertion la plus basse de 82,8 secondes a été obtenue en mode MSLAB activé, sur une table avec une seule partition, avec BlockCache activé, BlockSize = 16, des enregistrements de 10000 octets par unité.
Voyons maintenant le modÚle. Nous voyons une bonne qualité de modÚle selon R2, mais il est clair que l'extrapolation est déconseillée ici. Le comportement réel du systÚme lors de la variation des paramÚtres ne sera pas linéaire, ce modÚle n'est pas destiné à des prévisions, mais à comprendre ce qui s'est produit dans les limites des paramÚtres donnés. Par exemple, ici nous voyons selon le critÚre de Student, que pour l'opération Put, les paramÚtres BlockSize et BlockCache n'ont pas d'importance (ce qui est assez prévisible) :

Il est quelque peu inattendu que l'augmentation du nombre de partitions entraĂźne une baisse des performances (nous avons dĂ©jĂ vu un impact positif de l'augmentation du nombre de partitions lors de BulkLoad), bien que cela soit explicable. PremiĂšrement, le traitement nĂ©cessite de former des requĂȘtes pour 30 rĂ©gions au lieu d'une seule, et le volume de donnĂ©es n'est pas tel que cela en apporte un gain. DeuxiĂšmement, le temps d'exĂ©cution total est dĂ©terminĂ© par le RS le plus lent, et comme le nombre de DataNode est infĂ©rieur Ă celui des RS, certaines rĂ©gions n'ont aucune localitĂ©. Regardons donc les cinq leaders :

Ăvaluons maintenant les rĂ©sultats des blocs Get :

Le nombre de partitions a perdu de son importance, ce qui s'explique probablement par le fait que les donnĂ©es sont bien mises en cache et que le cache pour la lecture est le paramĂštre le plus significatif (statistiquement). Ăvidemment, l'augmentation du nombre de messages dans la requĂȘte est Ă©galement trĂšs bĂ©nĂ©fique pour les performances. Meilleurs rĂ©sultats :

Enfin, examinons le modÚle de bloc qui a d'abord exécuté get, puis put :

Tous les paramÚtres ici sont significatifs. Et les résultats des leaders :

9. Tests de charge
Enfin, exĂ©cutons une charge plus ou moins consĂ©quente, mais c'est toujours plus intĂ©ressant quand il y a quelque chose Ă comparer. Sur le site de DataStax â le dĂ©veloppeur clĂ© de Cassandra, il y a des NT d'une sĂ©rie de stockages NoSQL, y compris HBase version 0.98.6-1. Le chargement s'est fait avec 40 flux, taille des donnĂ©es 100 octets, disques SSD. Le rĂ©sultat des tests d'opĂ©rations Read-Modify-Write a montrĂ© tels rĂ©sultats.

D'aprĂšs ce que j'ai compris, la lecture Ă©tait effectuĂ©e par blocs de 100 enregistrements et pour 16 nĆuds, le test DataStax sur HBase a montrĂ© une performance de 10 000 opĂ©rations par seconde.
Il est heureux que notre cluster dispose Ă©galement de 16 nĆuds, mais il n'est pas trĂšs "heureux" que chacun ait 64 cĆurs (flux), tandis que dans le test de DataStax, il n'y en avait que 4. D'un autre cĂŽtĂ©, ils ont des disques SSD, tandis que nous avons des HDD et une version HBase plus rĂ©cente, et l'utilisation du CPU pendant la charge n'augmentait pratiquement pas (visuellement de 5 Ă 10 pour cent). Cependant, nous essayerons tout de mĂȘme de dĂ©marrer avec cette configuration. Les paramĂštres des tables sont par dĂ©faut, la lecture se fait sur une plage de clĂ©s de 0 Ă 50 millions de maniĂšre alĂ©atoire (c'est-Ă -dire Ă chaque fois un nouveau). Il y a 50 millions d'enregistrements dans la table, rĂ©partis sur 64 partitions. Les clĂ©s sont hachĂ©es par crc32. Les paramĂštres des tables sont par dĂ©faut, MSLAB est activĂ©. DĂ©marrage de 40 flux, chaque flux lit un ensemble de 100 clĂ©s alĂ©atoires et Ă©crit immĂ©diatement 100 octets gĂ©nĂ©rĂ©s sur ces clĂ©s.

Stand: 16 DataNodes et 16 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 flux). Version HBase 1.2.0-cdh5.14.2.
Le résultat moyen est proche de 40 000 opérations par seconde, ce qui est nettement mieux que dans le test de DataStax. Cependant, dans un souci d'expérimentation, il est possible de modifier légÚrement les conditions. Il est peu probable que tout le travail se fasse uniquement avec une seule table et seulement avec des clés uniques. Supposons qu'il existe un certain ensemble "chaud" de clés qui génÚre la principale charge. Nous allons donc essayer de créer une charge avec des enregistrements plus volumineux (10 Ko), également en paquets de 100, dans 4 tables différentes et en limitant la plage des clés demandées à 50 000. Le graphique ci-dessous montre le démarrage de 40 flux, chaque flux lit un ensemble de 100 clés et écrit immédiatement des 10 Ko aléatoires sur ces clés.

Stand: 16 DataNodes et 16 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 flux). Version HBase 1.2.0-cdh5.14.2.
Au cours de la charge, plusieurs exécutions de la compaction majeure ont été lancées, comme indiqué ci-dessus, sans cette procédure la performance se dégrade progressivement, mais durant l'exécution, une charge supplémentaire se produit également. Les baisses de performance sont causées par différentes raisons. Parfois, des flux terminaient leur travail et pendant qu'ils redémarraient, une pause survenait, parfois des applications tierces créaient une charge sur le cluster.
La lecture et l'Ă©criture simultanĂ©es constituent l'un des scĂ©narios de travail les plus exigeants pour HBase. Si vous faites uniquement des requĂȘtes put de petite taille, par exemple de 100 octets, en les regroupant en lots de 10 Ă 50 000, vous pouvez obtenir des centaines de milliers d'opĂ©rations par seconde, et il en va de mĂȘme pour les requĂȘtes en lecture seule. Il convient de noter que les rĂ©sultats sont radicalement meilleurs que ceux obtenus avec DataStax, principalement grĂące aux requĂȘtes en blocs de 50 000.

Stand: 16 DataNodes et 16 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 flux). Version HBase 1.2.0-cdh5.14.2.
10. Conclusions
Ce systĂšme est suffisamment flexible, mais l'influence d'un grand nombre de paramĂštres demeure encore inconnue. Certains d'entre eux ont Ă©tĂ© testĂ©s, mais n'ont pas Ă©tĂ© inclus dans l'ensemble de tests final. Par exemple, des expĂ©riences prĂ©liminaires ont montrĂ© que le paramĂštre DATA_BLOCK_ENCODING, qui code les informations en utilisant les valeurs des cellules voisines, a une signification lĂ©gĂšrement insignifiante, ce qui est tout Ă fait explicable pour des donnĂ©es gĂ©nĂ©rĂ©es de maniĂšre alĂ©atoire. Dans le cas oĂč un grand nombre d'objets rĂ©pĂ©titifs est utilisĂ©, le gain peut ĂȘtre significatif. En gĂ©nĂ©ral, on peut dire qu'HBase donne l'impression d'ĂȘtre une base de donnĂ©es assez sĂ©rieuse et rĂ©flĂ©chie, qui peut ĂȘtre suffisamment performante lors de l'exĂ©cution d'opĂ©rations sur de gros blocs de donnĂ©es, en particulier lorsqu'il est possible d'Ă©talonner dans le temps les processus de lecture et d'Ă©criture.
Si quelque chose ne vous semble pas assez clair, je suis prĂȘt Ă en parler plus en dĂ©tail. Nous vous invitons Ă partager vos expĂ©riences ou Ă discuter si vous n'ĂȘtes pas d'accord avec certains points.
Source : habr.com
