2013年2月28日木曜日

Hadoopチューニング


Hadoopを利用できる環境は整えた。

規模を大きくしていく場合に考量すべき最低限の
チューニング個所をまとめておく。

HDFS関連
hdfs-site.xml 内で設定
● ブロックサイズの設定
dfs.block.size
ディフォルト:67108864(64MB)
変更:134217728(128MB) ブロック数の削減、またその数を抑えてヒープメモリを抑える

● レプリケーション数の設定
dfs.replication
ディフォルト:3
変更:3 (そのままでいいでしょう)
必要なディスク容量は、単純に3倍になるのではなく、
MapReduceの中間データも書き込まれるので4倍程度は必要。

● DataNodeへの上限接続数
dfs.datanode.max.xcievers(0.2.0系) receiversの誤記らしい。。
dfs.datanode.max.transfer.threads(0.2.1系)
ディフォルト:256
変更:2048 (100台規模を想定時)


 TaskTracker関連
mapred-site.xml 内で設定
● TaskTrackerのワーカースレッド数
httpプロトコルを使い、maperの出力をreducerに供給する際に使われる。
tasktracker.http.threads(0.20系)
mapreduce.tasktracker.http.threads(0.21系)
ディフォルト:40
変更:80 (100台規模を想定時)


 map関連
mapred-site.xml 内で設定
● map処理での最大ヒープメモリ
mapred.child.java.opts(0.20系) map、reduce共通キー
mapreduce.map.java.opts(0.21系) 個別に設定が可能
ディフォルト:-Xmx200m
JavaのVM周りは後章でもう少し突っ込んで考えてみる。

● 起動数(スロット数)
mapred.tasktracker.map.tasks.maximum(0.2.0系)
mapreduce.tasktracker.map.tasks.maximum(0.2.1系)
ディフォルト:2
変更:コア数×1.5倍程度で設定すればよい

● mapの出力をソートする際に使用するメモリサイズ
内訳はレコード情報 + データ
レコード情報:データ領域内のデータの区切り情報
データ:データを格納する領域
io.sort.mb(0.20系)
mapreduce.task.io.sort.mb(0.21系)
ディフォルト:100(MB)
変更:検討中

● mapのソート結果をメモリからディスクへ書き出す割合
io.sort.spill.percent(0.20系)
mapreduce.map.sort.spill.percent(0.21系)
ディフォルト:0.8(80%)
変更:検討中

レコード情報のチューニングキーはなし(ディフォルトmap用メモリバッファの5%)

● ディスクに書き出されたmapのソート結果(セグメント)をマージする一回当たりのセグメント数
io.sort.factor(0.20系)
mapreduce.task.io.sort.factor(0.21系)
ディフォルト:100
変更:検討中


 reduce処理
mapred-site.xml 内で設定
● reduce処理での最大ヒープメモリ
mapred.child.java.opts(0.20系) map、reduce共通キー
mapreduce.reduce.java.opts(0.21系) 個別に設定が可能
ディフォルト:-Xmx200m

● 起動数(スロット数)
mapred.tasktracker.reduce.tasks.maximum(0.2.0系)
mapreduce.tasktracker.reduce.tasks.maximum(0.2.1系)
ディフォルト:2
変更:コア数×1.5倍程度で設定すればよい

● reduceタスクの実行タイミング
mapタスクの80%が終わってからreduceタスクを開始させる。
無駄にreducerを待たせない

mapred.reduce.slowstart.completed.maps(0.20系)
mapreduce.job.reduce.slowstart.completedmaps(0.21系)
ディフォルト:0.8
変更:検討中

● mapの出力をreducerが取得する際の並列コピー数
このコピー中にシャッフル処理もされる。
mapred.reduce.parallel.copies(0.20系)
mapreduce.reduce.shuffle.parallelcopies(0.21系)
ディフォルト:5(数/s)
変更:20 (500台規模を想定時)

(例)
mapの処理結果が多いと時間がかかる例である。
mapの処理結果数=10000
設定値:5
10000(数) / (5(数)/1(s)) = 20000(s)

● メモリからディスクへ書き出す割合
mapred.job.shuffle.input.buffer.percent(0.20系)
mapreduce.reduce.shuffle.input.buffer.percent(0.21系)
ディフォルト:0.7(70%)
変更:検討中

● ディスクにマージする前にメモリに取り込むソート済みmapの出力数
mapred.inmem.merge.threshold(0.20系)
mapreduce.reduce.merge.inmem.threshold(0.21系)
ディフォルト:1000(MB)
変更:検討中


 Java VM関連
● ヒープメモリ
/usr/local/hadoop/conf/hadoop-env.sh 内で設定する。
HADOOP_OPTS="-server -Xms2000m"
HADOOP_HEAPSIZE=2000 (-Xmxと同じ意味)

-Xms:初期ヒープサイズ
-Xmx:最大ヒープサイズ

Xmxで設定した値まで確実に利用するので起動時に最大まで割り当てたほうが効率がいい。
ヒープを大きくするとGCが長くなるので60GB程度を限度としたほうがいい。

・NameNode指針
このような構成例では
HDFSディレクトリ数:1
HDFSファイル数:2
HDFSブロック数: 12800MB(トータル容量とする) / 128MB = 100

NameNodeで必要なヒープメモリ:(1 + 2 + 100) * 150byte
ファイル、ディレクトリ、ブロックごとに150byteのヒープが必要である。

ブロックサイズ64MBはdfs.block.sizeキーの値で変わってくる。
当たり前だが、保存する日数分を考慮して計算すること。

・JobTracker指針
ジョブの履歴(保存数)が以下のキーで管理されているためその分が必要。
mapred-site.xml 内で設定
mapred.jobtracker.completeuserjobs.maximum(0.20系)
mapreduce.jobtracker.retiredjobs.cache.size(0.21系)
ディフォルト:1000

・DataNode
特に考慮不要

・TaskTracker
以下で設定した最大ヒープ数×起動数(スロット数)が必要とされる。

mapred-site.xml 内で設定

最大ヒープ数
mapred.child.java.opts(0.20系)
mapreduce.map.java.opts(0.21系)
mapreduce.reduce.java.opts(0.21系)
-Xmx200m

起動数(スロット数)
mapred.tasktracker.map.tasks.maximum(0.2.0系)
mapreduce.tasktracker.map.tasks.maximum(0.2.1系)

mapred.tasktracker.reduce.tasks.maximum(0.2.0系)
mapreduce.tasktracker.reduce.tasks.maximum(0.2.1系)



 GC
詳細は省略。通常のJavaJVM周りの設定値を見直せばよい。
指針としては、
NameNode、DataNodeはHDFSでファイルを管理する関係上、
New領域よりOld領域を多く。
JobTracker、TaskTrackerはジョブが終わればヒープは解放されるので
New領域を多くすればいい。



 OS側
kernelチューニング
ユーザ単位でのシステムリソース制限