Kleine Dateien sind ein großes Problem in Hadoop – oder zumindest sind sie es, wenn man von der Anzahl der Fragen auf der Benutzerliste zu diesem Thema ausgeht. In diesem Beitrag werde ich auf das Problem eingehen und einige gängige Lösungen untersuchen.
Probleme mit kleinen Dateien und HDFS
Eine kleine Datei ist eine Datei, die deutlich kleiner ist als die HDFS-Blockgröße (Standard 64 MB). Wenn Sie kleine Dateien speichern, haben Sie wahrscheinlich viele davon (sonst würden Sie sich nicht an Hadoop wenden), und das Problem ist, dass HDFS nicht mit vielen Dateien umgehen kann.
Jede Datei, jedes Verzeichnis und jeder Block in HDFS wird als Objekt im Speicher des Namensknotens dargestellt, von denen jedes als Faustregel 150 Bytes belegt. 10 Millionen Dateien, die jeweils einen Block verwenden, würden also etwa 3 Gigabyte Speicher belegen. Das Hochskalieren weit über dieses Niveau hinaus ist bei aktueller Hardware ein Problem. Sicherlich ist eine Milliarde Dateien nicht machbar.
Darüber hinaus ist HDFS nicht darauf ausgelegt, effizient auf kleine Dateien zuzugreifen:Es ist in erster Linie für den Streaming-Zugriff auf große Dateien ausgelegt. Das Durchlesen kleiner Dateien verursacht normalerweise viele Suchvorgänge und viele Sprünge von Datenknoten zu Datenknoten, um jede kleine Datei abzurufen, was alles ein ineffizientes Datenzugriffsmuster ist.
Probleme mit kleinen Dateien und MapReduce
Zuordnungsaufgaben verarbeiten normalerweise jeweils einen Eingabeblock (unter Verwendung des standardmäßigen FileInputFormat
). Wenn die Datei sehr klein ist und viele davon vorhanden sind, verarbeitet jede Zuordnungsaufgabe nur sehr wenig Eingaben, und es gibt viel mehr Zuordnungsaufgaben, die jeweils einen zusätzlichen Buchhaltungsaufwand verursachen. Vergleichen Sie eine 1-GB-Datei, die in 16 64-MB-Blöcke aufgeteilt ist, und etwa 10.000 100-KB-Dateien. Die 10.000 Dateien verwenden jeweils eine Zuordnung, und die Auftragszeit kann zehn- oder hundertmal langsamer sein als bei einer einzelnen Eingabedatei.
Es gibt ein paar Funktionen, die dabei helfen, den Buchhaltungsaufwand zu verringern:Task-JVM-Wiederverwendung zum Ausführen mehrerer Map-Tasks in einer JVM, wodurch ein gewisser JVM-Start-Overhead vermieden wird (siehe mapred.job.reuse.jvm.num.tasks
-Eigenschaft) und MultiFileInputSplit
die mehr als einen Split pro Karte ausführen kann.
Warum werden kleine Dateien erstellt?
Es gibt mindestens zwei Fälle
- Die Dateien sind Teile einer größeren logischen Datei. Da HDFS Anhänge erst seit kurzem unterstützt, besteht ein sehr verbreitetes Muster zum Speichern unbegrenzter Dateien (z. B. Protokolldateien) darin, sie in Blöcken in HDFS zu schreiben.
- Die Dateien sind von Natur aus klein. Stellen Sie sich einen großen Korpus von Bildern vor. Jedes Bild ist eine eigenständige Datei, und es gibt keine natürliche Möglichkeit, sie zu einer größeren Datei zusammenzufügen.
Diese beiden Fälle erfordern unterschiedliche Lösungen. Für den ersten Fall, wo die Datei aus Datensätzen besteht, kann das Problem vermieden werden, indem sync()
von HDFS aufgerufen wird Methode von Zeit zu Zeit kontinuierlich große Dateien zu schreiben. Alternativ ist es möglich, ein Programm zu schreiben, um die kleinen Dateien miteinander zu verketten.
Für den zweiten Fall wird eine Art Container benötigt, um die Dateien auf irgendeine Weise zu gruppieren. Hadoop bietet hier einige Optionen.
HAR-Dateien
Hadoop-Archive (HAR-Dateien) wurden in 0.18.0 in HDFS eingeführt, um das Problem zu verringern, dass viele Dateien den Speicher des Namensknotens belasten. HAR-Dateien funktionieren, indem sie ein mehrschichtiges Dateisystem auf HDFS aufbauen. Eine HAR-Datei wird mithilfe des hadoop archive
erstellt Befehl, der einen MapReduce-Job ausführt, um die zu archivierenden Dateien in eine kleine Anzahl von HDFS-Dateien zu packen. Für einen Client, der das HAR-Dateisystem verwendet, hat sich nichts geändert:Alle Originaldateien sind sichtbar und zugänglich (allerdings unter Verwendung eines har:// URL). Die Anzahl der Dateien in HDFS wurde jedoch reduziert.
Das Lesen von Dateien in einem HAR ist nicht effizienter als das Lesen von Dateien in HDFS und kann tatsächlich langsamer sein, da jeder HAR-Dateizugriff zwei Lesevorgänge für die Indexdatei sowie das Lesen der Datendatei erfordert (siehe Diagramm). Und obwohl HAR-Dateien als Eingabe für MapReduce verwendet werden können, gibt es keine spezielle Magie, die es Karten ermöglicht, über alle Dateien in der HAR-Co-Resident auf einem HDFS-Block zu arbeiten. Es sollte möglich sein, ein Eingabeformat zu erstellen, das die verbesserte Lokalität von Dateien in HARs nutzen kann, aber es existiert noch nicht. Beachten Sie, dass MultiFileInputSplit trotz der Verbesserungen in HADOOP-4565 zur Auswahl von knotenlokalen Dateien in einer Teilung eine Suche pro kleiner Datei benötigt. Es wäre interessant, die Leistung im Vergleich zu beispielsweise einer SequenceFile zu sehen. Derzeit werden HARs wahrscheinlich am besten nur für Archivierungszwecke verwendet.
Sequenzdateien
Die übliche Antwort auf Fragen zum „Problem mit kleinen Dateien“ lautet:Verwenden Sie eine SequenceFile. Die Idee dabei ist, dass Sie den Dateinamen als Schlüssel und den Dateiinhalt als Wert verwenden. Das funktioniert in der Praxis sehr gut. Um auf die 10.000 100-KB-Dateien zurückzukommen, können Sie ein Programm schreiben, um sie in eine einzelne SequenceFile zu packen, und Sie können sie dann im Streaming-Verfahren (direkt oder mit MapReduce) verarbeiten, indem Sie auf der SequenceFile arbeiten. Es gibt auch ein paar Boni. SequenceFiles sind aufteilbar, sodass MapReduce sie in Chunks aufteilen und auf jedem Chunk unabhängig arbeiten kann. Im Gegensatz zu HARs unterstützen sie auch die Komprimierung. Die Blockkomprimierung ist in den meisten Fällen die beste Option, da sie Blöcke mit mehreren Datensätzen komprimiert (und nicht pro Datensatz).
Es kann langsam sein, vorhandene Daten in SequenceFiles zu konvertieren. Es ist jedoch durchaus möglich, eine Sammlung von SequenceFiles parallel zu erstellen. (Stuart Sierra hat einen sehr nützlichen Beitrag über das Konvertieren einer tar-Datei in eine SequenceFile geschrieben – Tools wie dieses sind sehr nützlich, und es wäre gut, mehr davon zu sehen). In Zukunft ist es am besten, Ihre Datenpipeline so zu gestalten, dass die Daten an der Quelle nach Möglichkeit direkt in eine SequenceFile geschrieben werden, anstatt als Zwischenschritt in kleine Dateien zu schreiben.
Im Gegensatz zu HAR-Dateien gibt es keine Möglichkeit, alle Schlüssel in einer SequenceFile aufzulisten, außer die gesamte Datei zu lesen. (MapFiles, die wie SequenceFiles mit sortierten Schlüsseln sind, behalten einen Teilindex bei, sodass sie auch nicht alle ihre Schlüssel auflisten können – siehe Diagramm.)
SequenceFile ist eher Java-zentriert. TFile ist plattformübergreifend und ein Ersatz für SequenceFile, aber noch nicht verfügbar.
HBase
Wenn Sie viele kleine Dateien produzieren, ist je nach Zugriffsmuster möglicherweise eine andere Art der Speicherung geeigneter. HBase speichert Daten in MapFiles (indizierte SequenceFiles) und ist eine gute Wahl, wenn Sie Streaming-Analysen im MapReduce-Stil mit gelegentlichem zufälligem Nachschlagen durchführen müssen. Wenn die Latenz ein Problem darstellt, gibt es viele andere Möglichkeiten – siehe Richard Jones’ hervorragende Umfrage zu Key-Value-Stores.