halookで始めるHadoop/HBaseトラブルシューティング

第4回投機的実行

今回のテーマは投機的実行です。投機的実行は、MapやReduce Taskの処理の失敗を見越して、空いているリソースを使って同じTaskを実行するものです。HadoopのMapReduce Jobの特徴的な機能としてよく取り上げられますが、サンプルのJobを動かしてみる程度では、効果があるのか、そもそもこの機能が動いているのか、なかなか確認しづらい代物です。また「投機的実行を動かしてはいけない場面」も存在します。halookのキャプチャ画像を基に、投機的実行とはどういったものなのか、どういうときに実行されるのか、見ていくことにしましょう。

なお、本記事中のサンプルは、CDH3u5の、TaskTrackerノードが3台の環境で実行した結果です。各TaskTrackerのMap、Reduceスロット数はそれぞれデフォルトの2で設定されており、1つのジョブで同時に実行されるMapタスク数、Reduceタスク数は、それぞれ6となっています。

投機的実行とは?

まずは、投機的実行の例を見てください。図1は、全部で7個のMap Taskが実行されるJobを走らせた結果です。ご覧のように、m_00005 と書かれた行が2行ありますが、これが、同じTask ID のTask Attemptが2つ同時に実行された様子を表しています。片方が灰色になっているのは、Task Attemptの終了ステータスが「KILLED」になっているためです。

図1 投機的実行の発生
図1 投機的実行の発生

このように、

同じTaskのTask Attemptが同時に実行され、どちらかが先に終了した段階でもう一方がKillされる

というのが、投機的実行の動きです。

投機的実行には、以下のメリットがあります。

  1. Taskが失敗した場合、もう一度同じTaskを再実行することになるが、投機的実行が動いていれば最初からやり直す必要がない
  2. ノードごとの環境差分により、Taskの実行時間に差が出る場合、投機的実行が動いていれば、早く終わったほうの時間でJob全体が終了する

この通り、投機的実行は、普段のJob実行を高速化するような、そういった機能ではありません。何か予測しない問題が起こったときの影響を最小限にするための機能です。また、図1の例のように、Jobの前半部分では投機的実行は起こりません。6つのMap Slotが全て使われている前半では投機的実行は行われず、後半の、Slotに余裕があるときのみ、実行されるのです。あくまで空いているリソースを使う仕組みのため、Jobの実行時間が遅くなるような影響を出す心配はありません。

ただし、投機的実行をONにしてはいけない場面もあります。たとえば、Map Task処理中に直接ファイルを操作したり、外部のDBやHBaseへ書き込みを行ったりするケースです。このような場面では、投機的実行が走ると、意図せず2箇所からの同時書き込みでデータを破壊する可能性があります。また、複数のJobを同時に動かすことを想定しているために、空いているからといってSlotを余計に使われると困る場合もあるでしょう。このような場合は、投機的実行を確実にOFFにする必要があります。

一度、⁠設定の不備で、投機的実行をOFFにしたつもりがOFFになっていなかった」という現場に遭遇したことがあります。この後示す通り、投機的実行が行われるにはいくつかの条件があるため、ごく単純なJobを動かしただけで「投機的実行は行われていないな」と判断するのではなく、条件を整えた上で動作を確認するよう注意しましょう。

投機的実行が行われる条件

あるTaskのTask Attempt実行中に、投機的実行により同じTaskのTask Attemptがもう一つ開始する条件は、以下の通りです。

  1. 現在実行中のTask Attemptが自分自身のみ
  2. Jobの開始から1分以上経過している
  3. 全Taskの平均の進捗(%)より、20%以上進捗が遅れている

1により、3つ以上のTask Attemptが同時に走ることはありません。

2があるため、短いJobでは投機的実行は起こりません。図2は、図1とほとんど同じ処理ですが、後半のMap Taskがジョブの開始時刻から60秒以内にスタートしているため、投機的実行は行われません。

なお、より細かい話をすると、この投機的実行開始の判定処理は、⁠Taskの進捗が増えるタイミング」で行われます。例えば、Map処理で言えば、読み込んだ1行分の処理が完了して次の行の処理に移るタイミングです。図2の例では、わかりやすくするためにTaskの開始と終了のタイミング以外で進捗が増えないようになっているため、後半のTaskの途中でも投機的実行は開始していません。

図2 経過時間が短く投機的実行が行われない例
図2 経過時間が短く投機的実行が行われない例

投機的実行は、複数のTaskで同時に起こることもあります。図3は、2つのTaskで投機的実行が走ったときのもので、灰色の矢印が2本表示されていることからそれがわかります。

図3 2つのTaskで投機的実行が行われている例
図3 2つのTaskで投機的実行が行われている例

スロット数を超えてMap処理が同時に実行されることはないため、空いているslotが少ないときは一部のTaskだけが投機的実行の対象になります。図4では、図3のときと違い1つのTaskのみが投機的実行の対象になっています。

図4 一部のTaskのみが投機的実行の対象になった場合
図4 一部のTaskのみが投機的実行の対象になった場合

3番目の、⁠全Taskの平均の進捗(%)より、20%以上進捗が遅れている」は以下の例を見るとわかります。

図5 他と比べて遅いTaskがあった場合
図5 他と比べて遅いTaskがあった場合

図5では、最後の時間のかかっているTaskの投機的実行が、他のTaskが終了したタイミングから走り始めていることがわかります。最後のTaskは、他のTaskが終了したタイミングで、まだ50%しか終わっていません。全Map処理の進捗がほぼ100%に達している状態のため、⁠20%以上進捗が遅れている」の条件に合致します。

まとめ

いかがでしたか?投機的実行は、普段あまり意識することはないかもしれませんが、⁠複数のJobを同時に動かしたいから、投機的実行でSlotを使って欲しくない」というように、実際の動きを知りたくなる場面も運用上出てきます。仕組みを理解して、普段動かしているJobでどのように投機的実行が行われているか確認してみてはいかがでしょうか。

今回はここまでです。次回もお楽しみに。

おすすめ記事

記事・ニュース一覧