Amazon Elastic MapReduceの使い方─Hadoopより手軽にはじめる大規模計算

第7回Amazon Elastic MapReduceのパフォーマンスを引き出すためのHadoopの基礎知識

Amazon Elastic Mapreduce(EMR)は、Hadoop環境を構築することなくMapReduceが使える、PaaSに近いものです。しかし、中身はAmazonが独自にカスタマイズしているHadoopなので、一般的なHadoopのチューニング手法をそのまま適用できない場合があったりします。

前回の予告とは若干異なりますが、今回はいったんEMRを離れてHadoopの基礎を説明します。順番的にも先にこちらを説明したほうが内部のしくみもわかってより理解しやすいと思います。

HDFSではサイズが大きいファイルを扱うほうが効率的

あえて今回まで触れませんでしたが、Hadoopとは言わずと知れたGoogleの論文をもとに作成されたGFS(Google File System)とMapReduceのオープンソースのクローンです。GFSはHDFS(Hadoop Distributed File System)という名称に置き換えられています。パフォーマンスの話ではこのHDFSが肝になってきます。

HDFSは、Hadoop Distributed File Systemの名称どおり、分散ファイルシステムです。HDFSではデータが各ノードにブロック単位ごとに保存されます。デフォルトのレプリケーション数は3となっています。

ブロックの意味は、基本的に普通のファイルシステムと同様に考えてください。ただし、通常のファイルシステムと違うのがデフォルトのサイズの大きさです。たとえばext3では1024、2048、4096バイトなどを指定するのに対し、HDFSではデフォルトが64MBとかなり大きい値になっています(Hadoopのディストリビューションを提供しているCloudera社では128MBを推奨しているようです⁠⁠。

なぜブロックサイズがこのように大きいかといえば、Hadoopでは大容量のファイルを扱うのが目的であるためです。Hadoopを使用する際は、サイズが小さいファイルを複数用意するよりも、大きいファイル(GB以上)を扱う方が効率的です

小さいファイルを複数扱うとNameNodeでメモリオーバーという問題が起きますが、ここでは割愛させていただきます。

Map数とReduce数はどのように決まるのか

もう1つのMapReduceは、かんたんに言えばMapとReduceを複数のノードに分散することで、単一のサーバで実行するよりも高速に処理するフレームワークです。

MapとReduceでは、分散のされ方が以下のように異なります。

Map
→splitという単位で分散され、ユーザが特に意識することはない
Reduce
→実際にReduceさせる数をユーザが指定する。デフォルト値は1(1つしかReduceが起動されない)

なぜReduceはユーザが指定するのかというと、Reduce数はMapにより渡されるキーによって決まるため、Hadoop側では判断ができないからです。ユーザの設定次第となってしまいます。

splitの単位を指定する場合

Map数はsplitという単位で分散されると前述しましたが、splitの単位はユーザが設定することができます。具体的には以下の2つの方法があります。

①mapred-site.xmlに下記のように記述する

mapred.min.split.size:最小splitサイズ
mapred.max.split.size:最大splitサイズ

②コード中で指定する(以下はJavaの場合)

FileInputFormat.setMinInputSplitSize(job, minSize);
FileInputFormat.setMaxInputSplitSize(job, minSize);

何も指定しないのであれば、デフォルトは「HDFSブロックサイズ=splitサイズ」となります。

では、splitサイズを指定した場合、どのように実際のサイズが決まるのでしょうか。その挙動を定義したのか以下のコードです。

protected long computeSplitSize(long blockSize, long minSize, long maxSize) {
  return Math.max(minSize, Math.min(maxSize, blockSize));
}

かんたんにまとめると以下のようになります。

  • 指定した最大値とブロックサイズの小さいほうを比較する
  • 出た値と指定した最小値の大きいほうに決める

EMRでMap数を増やすには

ここまで読んで気づいたかもしれませんが、デフォルトでは「HDFSのブロックサイズ=split数」です。EMRの場合はHDFSではなくS3を使用するので、⁠S3のファイル数=split数」となってしまいます。

たとえば1GBのファイルをMapReduceで処理する場合を考えてみましょう。HDFSでは、1GBのファイルがデフォルトでは64MBのブロック単位で各ノードに分散されているので、その値でMapが起動されます。

一方S3の場合は、1GBのファイルがそのままブロック数になるので、Mapは1つしか起動されません。これではせっかくの分散処理もまったく意味がありません。

以上のことから、EMRでMap数を増やすには以下のようにすることをお勧めします。

  • mapred-site.xmlに適切なsplit値を設定し、bootstrapでEMRを設定する。Javaなどのコードであれば、InputFormatで適切なsplit値を設定する
  • 大きいファイルであれば、S3に上げるときに、HDFSと同様、ブロックとしてファイルを細かく分ける

以上、前回のbootstrapよりもHadoopの基礎的な部分を中心に説明してきました。このあたりを押さえず、単純に使うだけではパフォーマンスは出ません。くわしい内容は、ぜひ他の文献を参考にしてください。

次回は前回の予告どおり、bootstrapにも絡むEMRの内部のパフォーマンスについて説明します。

おすすめ記事

記事・ニュース一覧