Les petits fichiers sont un gros problème dans Hadoop - ou, du moins, ils le sont si le nombre de questions sur la liste des utilisateurs sur ce sujet est quelque chose à dire. Dans cet article, je vais examiner le problème et examiner quelques solutions courantes.
Problèmes avec les petits fichiers et HDFS
Un petit fichier est un fichier nettement plus petit que la taille du bloc HDFS (64 Mo par défaut). Si vous stockez de petits fichiers, vous en avez probablement beaucoup (sinon vous ne vous tourneriez pas vers Hadoop), et le problème est que HDFS ne peut pas gérer beaucoup de fichiers.
Chaque fichier, répertoire et bloc dans HDFS est représenté comme un objet dans la mémoire du namenode, chacun occupant 150 octets, en règle générale. Ainsi, 10 millions de fichiers, chacun utilisant un bloc, utiliseraient environ 3 gigaoctets de mémoire. La mise à l'échelle bien au-delà de ce niveau est un problème avec le matériel actuel. Un milliard de fichiers n'est certainement pas envisageable.
De plus, HDFS n'est pas conçu pour accéder efficacement aux petits fichiers :il est principalement conçu pour l'accès en continu de fichiers volumineux. La lecture de petits fichiers entraîne normalement de nombreuses recherches et de nombreux sauts d'un nœud de données à l'autre pour récupérer chaque petit fichier, ce qui constitue un modèle d'accès aux données inefficace.
Problèmes avec les petits fichiers et MapReduce
Les tâches de carte traitent généralement un bloc d'entrée à la fois (en utilisant le FileInputFormat
par défaut ). Si le fichier est très petit et qu'il y en a beaucoup, chaque tâche de carte traite très peu d'entrées et il y a beaucoup plus de tâches de carte, chacune d'entre elles imposant une surcharge de comptabilité supplémentaire. Comparez un fichier de 1 Go divisé en 16 blocs de 64 Mo et environ 10 000 fichiers de 100 Ko. Les 10 000 fichiers utilisent chacun une carte, et le temps de travail peut être des dizaines ou des centaines de fois plus lent que l'équivalent avec un seul fichier d'entrée.
Il existe quelques fonctionnalités pour aider à réduire les frais généraux de comptabilité :réutilisation de la tâche JVM pour exécuter plusieurs tâches de mappage dans une JVM, évitant ainsi certains frais généraux de démarrage de la JVM (voir le mapred.job.reuse.jvm.num.tasks
propriété), et MultiFileInputSplit
qui peut exécuter plus d'une division par carte.
Pourquoi les petits fichiers sont-ils produits ?
Il y a au moins deux cas
- Les fichiers sont des éléments d'un fichier logique plus volumineux. Étant donné que HDFS n'a pris en charge que récemment les ajouts, un modèle très courant pour enregistrer des fichiers illimités (par exemple, des fichiers journaux) consiste à les écrire en morceaux dans HDFS.
- Les fichiers sont intrinsèquement petits. Imaginez un grand corpus d'images. Chaque image est un fichier distinct et il n'existe aucun moyen naturel de les combiner en un seul fichier plus volumineux.
Ces deux cas appellent des solutions différentes. Pour le premier cas, où le fichier est composé d'enregistrements, le problème peut être évité en appelant la fonction sync()
de HDFS. de temps en temps pour écrire en continu des fichiers volumineux. Alternativement, il est possible d'écrire un programme pour concaténer les petits fichiers ensemble.
Dans le second cas, une sorte de conteneur est nécessaire pour regrouper les fichiers d'une manière ou d'une autre. Hadoop propose ici quelques options.
Fichiers HAR
Les archives Hadoop (fichiers HAR) ont été introduites dans HDFS dans la version 0.18.0 pour atténuer le problème de nombreux fichiers mettant la pression sur la mémoire du namenode. Les fichiers HAR fonctionnent en créant un système de fichiers en couches au-dessus de HDFS. Un fichier HAR est créé à l'aide de l'hadoop archive
, qui exécute une tâche MapReduce pour regrouper les fichiers archivés dans un petit nombre de fichiers HDFS. Pour un client utilisant le système de fichiers HAR, rien n'a changé :tous les fichiers d'origine sont visibles et accessibles (bien qu'en utilisant un har:// URL). Cependant, le nombre de fichiers dans HDFS a été réduit.
La lecture de fichiers dans un HAR n'est pas plus efficace que la lecture de fichiers dans HDFS, et en fait peut être plus lente car chaque accès au fichier HAR nécessite deux lectures de fichier d'index ainsi que la lecture du fichier de données (voir schéma). Et bien que les fichiers HAR puissent être utilisés comme entrée pour MapReduce, il n'y a pas de magie spéciale qui permette aux cartes de fonctionner sur tous les fichiers du co-résident HAR sur un bloc HDFS. Il devrait être possible de créer un format d'entrée pouvant tirer parti de la localisation améliorée des fichiers dans les HAR, mais il n'existe pas encore. Notez que MultiFileInputSplit, même avec les améliorations apportées à HADOOP-4565 pour choisir des fichiers dans une division qui sont des nœuds locaux, nécessitera une recherche par petit fichier. Il serait intéressant de voir les performances de ceci par rapport à un SequenceFile, par exemple. À l'heure actuelle, les HAR sont probablement mieux utilisés uniquement à des fins d'archivage.
Fichiers de séquence
La réponse habituelle aux questions sur « le problème des petits fichiers » est :utilisez un SequenceFile. L'idée ici est que vous utilisez le nom du fichier comme clé et le contenu du fichier comme valeur. Cela fonctionne très bien dans la pratique. Pour en revenir aux 10 000 fichiers de 100 Ko, vous pouvez écrire un programme pour les placer dans un seul SequenceFile, puis vous pouvez les traiter en continu (directement ou à l'aide de MapReduce) en opérant sur le SequenceFile. Il y a aussi quelques bonus. Les SequenceFiles sont divisibles, donc MapReduce peut les diviser en morceaux et opérer sur chaque morceau indépendamment. Ils prennent également en charge la compression, contrairement aux HAR. La compression par bloc est la meilleure option dans la plupart des cas, car elle compresse des blocs de plusieurs enregistrements (plutôt que par enregistrement).
La conversion des données existantes en SequenceFiles peut être lente. Cependant, il est parfaitement possible de créer une collection de SequenceFiles en parallèle. (Stuart Sierra a écrit un article très utile sur la conversion d'un fichier tar en SequenceFile - des outils comme celui-ci sont très utiles, et il serait bon d'en voir plus). À l'avenir, il est préférable de concevoir votre pipeline de données pour écrire les données à la source directement dans un SequenceFile, si possible, plutôt que d'écrire dans de petits fichiers comme étape intermédiaire.
Contrairement aux fichiers HAR, il n'y a aucun moyen de répertorier toutes les clés dans un SequenceFile, à moins de lire l'intégralité du fichier. (Les MapFiles, qui sont comme les SequenceFiles avec des clés triées, conservent un index partiel, de sorte qu'ils ne peuvent pas non plus répertorier toutes leurs clés - voir le schéma.)
SequenceFile est plutôt centré sur Java. TFile est conçu pour être multiplateforme et remplacer SequenceFile, mais il n'est pas encore disponible.
HBase
Si vous produisez beaucoup de petits fichiers, selon le modèle d'accès, un type de stockage différent peut être plus approprié. HBase stocke les données dans MapFiles (SequenceFiles indexés) et constitue un bon choix si vous devez effectuer des analyses en continu de style MapReduce avec une recherche aléatoire occasionnelle. Si la latence est un problème, alors il y a beaucoup d'autres choix - voir l'excellente enquête de Richard Jones sur les magasins à valeur clé.