小さなファイルはHadoopの大きな問題です。少なくとも、このトピックのユーザーリストにある質問の数が何であれ、それは問題です。この投稿では、問題を調べ、いくつかの一般的な解決策を検討します。
小さなファイルとHDFSの問題
小さなファイルとは、HDFSブロックサイズ(デフォルトは64MB)よりも大幅に小さいファイルです。小さなファイルを保存している場合は、おそらくそれらがたくさんあり(そうでなければ、Hadoopに頼らないでしょう)、問題は、HDFSが多くのファイルを処理できないことです。
HDFS内のすべてのファイル、ディレクトリ、およびブロックは、経験則として、ネームノードのメモリ内のオブジェクトとして表され、それぞれが150バイトを占有します。したがって、それぞれがブロックを使用する1,000万のファイルは、約3ギガバイトのメモリを使用します。このレベルをはるかに超えてスケールアップすることは、現在のハードウェアの問題です。確かに、10億のファイルは実行可能ではありません。
さらに、HDFSは、小さなファイルに効率的にアクセスするようには調整されていません。主に、大きなファイルのストリーミングアクセス用に設計されています。小さなファイルを読み取ると、通常、データノードからデータノードに多くのシークとホッピングが発生し、各小さなファイルが取得されます。これらはすべて、非効率的なデータアクセスパターンです。
小さなファイルとMapReduceの問題
マップタスクは通常、一度に入力のブロックを処理します(デフォルトの FileInputFormat
を使用) )。ファイルが非常に小さく、それらが多数ある場合、各マップタスクはほとんど入力を処理せず、さらに多くのマップタスクがあり、それぞれが余分な簿記のオーバーヘッドを課します。 1GBのファイルを16個の64MBブロックに分割し、10,000個程度の100KBファイルを比較します。 10,000個のファイルはそれぞれ1つのマップを使用し、ジョブ時間は単一の入力ファイルを使用する同等のファイルよりも数十倍または数百倍遅くなる可能性があります。
簿記のオーバーヘッドを軽減するのに役立つ機能がいくつかあります。1つのJVMで複数のマップタスクを実行するためのタスクJVMの再利用により、JVMの起動オーバーヘッドを回避します( mapred.job.reuse.jvm.num.tasks を参照)。コード> プロパティ)、および
MultiFileInputSplit
マップごとに複数の分割を実行できます。
小さなファイルが作成されるのはなぜですか?
少なくとも2つのケースがあります
- ファイルは、より大きな論理ファイルの一部です。 HDFSは最近追加をサポートしたばかりなので、制限のないファイル(ログファイルなど)を保存するための非常に一般的なパターンは、それらをチャンクでHDFSに書き込むことです。
- ファイルは本質的に小さいです。画像の大きなコーパスを想像してみてください。各画像は個別のファイルであり、それらを1つの大きなファイルに結合する自然な方法はありません。
これらの2つのケースでは、異なるソリューションが必要です。ファイルがレコードで構成されている最初のケースでは、HDFSの sync()
を呼び出すことで問題を回避できます。 大きなファイルを継続的に書き込む方法。または、小さなファイルを連結するプログラムを作成することもできます。
2番目のケースでは、何らかの方法でファイルをグループ化するために、ある種のコンテナーが必要です。 Hadoopはここでいくつかのオプションを提供します。
HARファイル
Hadoopアーカイブ(HARファイル)は0.18.0でHDFSに導入され、ネームノードのメモリに圧力をかける大量のファイルの問題を軽減しました。 HARファイルは、HDFS上に階層化されたファイルシステムを構築することで機能します。 HARファイルは、hadoopアーカイブ
を使用して作成されます コマンド。MapReduceジョブを実行して、アーカイブされているファイルを少数のHDFSファイルにパックします。 HARファイルシステムを使用しているクライアントには何も変更されていません。元のファイルはすべて表示され、アクセスできます( har:// を使用している場合でも) URL)。ただし、HDFSのファイル数は削減されています。
HARでのファイルの読み取りは、HDFSでのファイルの読み取りよりも効率的ではなく、実際には、各HARファイルアクセスに2つのインデックスファイルの読み取りとデータファイルの読み取りが必要になるため、速度が低下する可能性があります(図を参照)。また、HARファイルはMapReduceへの入力として使用できますが、HDFSブロックに共存するHAR内のすべてのファイルに対してマップを操作できるようにする特別な魔法はありません。 HAR内のファイルの局所性の向上を利用できる入力形式を構築することは可能であるはずですが、まだ存在していません。 MultiFileInputSplitは、ノードローカルである分割内のファイルを選択するためのHADOOP-4565の改善があっても、小さなファイルごとにシークする必要があることに注意してください。たとえば、SequenceFileと比較した場合のパフォーマンスを確認するのは興味深いことです。現時点では、HARはおそらく純粋にアーカイブ目的で使用するのが最適です。
シーケンスファイル
「小さなファイルの問題」に関する質問に対する通常の回答は、SequenceFileを使用することです。ここでの考え方は、ファイル名をキーとして使用し、ファイルの内容を値として使用することです。これは実際には非常にうまく機能します。 10,000個の100KBファイルに戻ると、それらを単一のSequenceFileに入れるプログラムを作成し、SequenceFileを操作するストリーミング方式(直接またはMapReduceを使用)でそれらを処理できます。いくつかのボーナスもあります。 SequenceFilesは分割可能であるため、MapReduceはそれらをチャンクに分割し、各チャンクを個別に操作できます。 HARとは異なり、圧縮もサポートしています。ブロック圧縮は、(レコードごとではなく)複数のレコードのブロックを圧縮するため、ほとんどの場合に最適なオプションです。
既存のデータをSequenceFilesに変換するのに時間がかかる場合があります。ただし、SequenceFilesのコレクションを並行して作成することは完全に可能です。 (Stuart Sierraは、tarファイルをSequenceFileに変換することについて非常に役立つ投稿を書いています—このようなツールは非常に便利であり、それらをもっと見るとよいでしょう)。今後は、中間ステップとして小さなファイルに書き込むのではなく、可能であれば、ソースのデータを直接SequenceFileに書き込むようにデータパイプラインを設計することをお勧めします。
HARファイルとは異なり、ファイル全体を読み取る以外に、SequenceFile内のすべてのキーを一覧表示する方法はありません。 (ソートされたキーを持つSequenceFilesのようなMapFilesは、部分インデックスを維持するため、すべてのキーを一覧表示することもできません。図を参照してください。)
SequenceFileはかなりJava中心です。 TFileはクロスプラットフォームであり、SequenceFileの代わりになるように設計されていますが、まだ利用できません。
HBase
小さなファイルを大量に作成する場合は、アクセスパターンに応じて、別の種類のストレージがより適切な場合があります。 HBaseはデータをMapFiles(インデックス付きSequenceFiles)に格納します。これは、ときどきランダムにルックアップしてMapReduceスタイルのストリーミング分析を行う必要がある場合に適しています。レイテンシーが問題になる場合は、他にも多くの選択肢があります。RichardJonesによるKey-Valueストアの優れた調査をご覧ください。