Présentation
Apache HBase est le gestionnaire de stockage Hadoop open-source, distribué et versionné bien adapté pour aléatoire , lecture/écriture en temps réel accès.
Attendre attendre? un accès aléatoire en lecture/écriture en temps réel ?
Comment est-ce possible ? Hadoop n'est-il pas simplement un système de traitement séquentiel en lecture/écriture par lots ?
Oui, nous parlons de la même chose, et dans les prochains paragraphes, je vais vous expliquer comment HBase réalise les E/S aléatoires, comment il stocke les données et l'évolution du format HFile de HBase.
Formats de fichier d'E/S Apache Hadoop
Hadoop est livré avec un format de fichier SequenceFile[1] que vous pouvez utiliser pour ajouter vos paires clé/valeur, mais en raison de la capacité d'ajout uniquement de hdfs, le format de fichier ne peut pas autoriser la modification ou la suppression d'une valeur insérée. La seule opération autorisée est l'ajout, et si vous souhaitez rechercher une clé spécifiée, vous devez lire le fichier jusqu'à ce que vous trouviez votre clé.
Comme vous pouvez le voir, vous êtes obligé de suivre le modèle de lecture/écriture séquentielle... mais comment est-il possible de construire un système d'accès en lecture/écriture aléatoire à faible latence comme HBase en plus ?
Pour vous aider à résoudre ce problème, Hadoop a un autre format de fichier, appelé MapFile[1], une extension de SequenceFile. Le MapFile, en réalité, est un répertoire qui contient deux SequenceFiles :le fichier de données « /data » et le fichier d'index « /index ». Le MapFile vous permet d'ajouter des paires clé/valeur triées et toutes les N clés (où N est un intervalle configurable), il stocke la clé et le décalage dans l'index. Cela permet une recherche assez rapide, car au lieu d'analyser tous les enregistrements, vous analysez l'index qui contient moins d'entrées. Une fois que vous avez trouvé votre bloc, vous pouvez alors sauter dans le vrai fichier de données.
MapFile est agréable car vous pouvez rechercher rapidement des paires clé/valeur, mais il y a toujours deux problèmes :
- Comment puis-je supprimer ou remplacer une clé/valeur ?
- Lorsque mon entrée n'est pas triée, je ne peux pas utiliser MapFile.
HBase et MapFile
La clé HBase comprend :la clé de ligne, la famille de colonne, le qualificateur de colonne, l'horodatage et un type.
Pour résoudre le problème de la suppression des paires clé/valeur, l'idée est d'utiliser le champ "type" pour marquer la clé comme supprimée (marqueurs tombstone). Pour résoudre le problème du remplacement des paires clé/valeur, il suffit de choisir le dernier horodatage (la valeur correcte est proche de la fin du fichier, ajouter signifie uniquement que la dernière insertion est proche de la fin).
Pour résoudre le problème des clés « non ordonnées », nous gardons en mémoire les dernières valeurs-clés ajoutées. Lorsque vous avez atteint un seuil, HBase le vide dans un MapFile. De cette façon, vous finissez par ajouter des clés/valeurs triées à un MapFile.
HBase fait exactement ceci[2] :lorsque vous ajoutez une valeur avec table.put(), votre clé/valeur est ajoutée au MemStore (sous le capot, MemStore est un ConcurrentSkipListMap trié). Lorsque le seuil par memstore (hbase.hregion.memstore.flush.size) est atteint ou que le RegionServer utilise trop de mémoire pour les memstores (hbase.regionserver.global.memstore.upperLimit), les données sont vidées sur le disque en tant que nouveau MapFile .
Le résultat de chaque vidage est un nouveau MapFile, et cela signifie que pour trouver une clé, vous devez chercher dans plus d'un fichier. Cela prend plus de ressources et est potentiellement plus lent.
Chaque fois qu'un get ou un scan est émis, HBase scanne chaque fichier pour trouver le résultat, pour éviter de sauter trop de fichiers, il y a un thread qui détectera quand vous aurez atteint un certain nombre de fichiers (hbase.hstore.compaction .max). Il essaie ensuite de les fusionner dans un processus appelé compactage, qui crée essentiellement un nouveau fichier volumineux à la suite de la fusion de fichiers.
HBase a deux types de compactage :l'un appelé « compactage mineur » qui fusionne simplement deux ou plusieurs petits fichiers en un seul, et l'autre appelé « compactage majeur » qui récupère tous les fichiers de la région, les fusionne et effectue un nettoyage. Dans un compactage majeur, les clés/valeurs supprimées sont supprimées, ce nouveau fichier ne contient pas les marqueurs de désactivation et toutes les clés/valeurs en double (opérations de remplacement de valeur) sont supprimées.
Jusqu'à la version 0.20, HBase utilisait le format MapFile pour stocker les données, mais dans la version 0.20, un nouveau MapFile spécifique à HBase a été introduit (HBASE-61).
HFile v1
Dans HBase 0.20, MapFile est remplacé par HFile :une implémentation de fichier de carte spécifique pour HBase. L'idée est assez similaire à MapFile, mais elle ajoute plus de fonctionnalités qu'un simple fichier clé/valeur. Des fonctionnalités telles que la prise en charge des métadonnées et de l'index sont désormais conservées dans le même fichier.
Les blocs de données contiennent les clés/valeurs réelles sous forme de MapFile. Pour chaque "opération de fermeture de bloc", la première clé est ajoutée à l'index, et l'index est écrit à la fermeture de HFile.
Le format HFile ajoute également deux types de blocs de « métadonnées » supplémentaires :Meta et FileInfo. Ces deux blocs clé/valeur sont écrits à la fermeture du fichier.
Le bloc Meta est conçu pour conserver une grande quantité de données avec sa clé sous forme de chaîne, tandis que FileInfo est une simple carte préférée pour les petites informations avec des clés et des valeurs qui sont toutes deux des tableaux d'octets. StoreFile de Regionserver utilise des méta-blocs pour stocker un filtre Bloom et FileInfo pour Max SequenceId, la clé de compactage majeure et les informations sur la plage de temps. Cette information est utile pour éviter de lire le fichier s'il n'y a aucune chance que la clé soit présente (Bloom Filter), si le fichier est trop ancien (Max SequenceId) ou si le fichier est trop récent (Timerange) pour contenir ce que l'on recherche pour.
HFile v2
Dans HBase 0.92, le format HFile a été légèrement modifié (HBASE-3857) pour améliorer les performances lorsque de grandes quantités de données sont stockées. L'un des principaux problèmes avec le HFile v1 est que vous devez charger tous les index monolithiques et les grands filtres Bloom en mémoire, et pour résoudre ce problème, la v2 introduit des index à plusieurs niveaux et un filtre Bloom au niveau du bloc. Par conséquent, HFile v2 offre une vitesse, une mémoire et une utilisation du cache améliorées.
La principale caractéristique de cette v2 sont les "blocs en ligne", l'idée est de casser l'index et le filtre Bloom par bloc, au lieu d'avoir l'index et le filtre Bloom entiers de l'ensemble du fichier en mémoire. De cette façon, vous pouvez conserver dans la RAM uniquement ce dont vous avez besoin.
Puisque l'index est déplacé au niveau du bloc, vous avez alors un index à plusieurs niveaux, ce qui signifie que chaque bloc a son propre index (index feuille). La dernière clé de chaque bloc est conservée pour créer l'intermédiaire/l'index qui fait ressembler l'arbre b+ à plusieurs niveaux.
L'entête du bloc contient désormais quelques informations :Le champ « Block Magic » a été remplacé par le champ « Block Type » qui décrit le contenu du bloc « Data », Leaf-Index, Bloom, Metadata, Root-Index, etc. des champs (taille compressée/non compressée et bloc de décalage précédent) ont été ajoutés pour permettre des recherches rapides en arrière et en avant.
Encodages des blocs de données
Étant donné que les clés sont triées et généralement très similaires, il est possible de concevoir une meilleure compression que ce qu'un algorithme à usage général peut faire.
HBASE-4218 a essayé de résoudre ce problème, et dans HBase 0.94, vous pouvez choisir entre deux algorithmes différents :Prefix et Diff Encoding.
L'idée principale de Prefix Encoding est de stocker le préfixe commun une seule fois, car les lignes sont triées et le début est généralement le même.
Le Diff Encoding pousse ce concept plus loin. Au lieu de considérer la clé comme une séquence opaque d'octets, le Diff Encoder divise chaque champ de clé afin de mieux compresser chaque partie. Cela étant, la famille de colonnes est stockée une seule fois. Si la longueur de la clé, la longueur de la valeur et le type sont identiques à ceux de la ligne précédente, le champ est omis. De plus, pour une compression accrue, l'horodatage est stocké sous forme de différence par rapport au précédent.
Notez que cette fonctionnalité est désactivée par défaut car l'écriture et la numérisation sont plus lentes, mais davantage de données sont mises en cache. Pour activer cette fonctionnalité, vous pouvez définir DATA_BLOCK_ENCODING =PREFIX | DIFF | FAST_DIFF dans les informations du tableau.
HFile v3
HBASE-5313 contient une proposition de restructuration de la mise en page HFile pour améliorer la compression :
- Regroupez toutes les clés ensemble au début du bloc et toutes les valeurs ensemble à la fin du bloc. De cette façon, vous pouvez utiliser deux algorithmes différents pour compresser la clé et les valeurs.
- Compressez les horodatages en utilisant le XOR avec la première valeur et utilisez VInt au lieu de long.
De plus, un format en colonne ou un encodage en colonne est à l'étude, jetez un œil à AVRO-806 pour un format de fichier en colonne par Doug Cutting.
Comme vous pouvez le voir, la tendance à l'évolution est d'être plus conscient de ce que contient le fichier, d'obtenir une meilleure compression ou une meilleure connaissance de l'emplacement qui se traduit par moins de données à écrire/lire à partir du disque. Moins d'E/S signifie plus de vitesse !
[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/