Map・Shuffle・Reduce を理解する

ビッグデータBeginner
オンラインで実践に進む

はじめに

小売業者の商品ファイルが整理され、アクセス可能になりました。これで最初の実験の疑問、「どの商品ラベルが最も多く登場するか?」に答えられます。数行なら手で数えられますが、増え続けるデータには、繰り返し実行できる計算が必要です。

MapReduce はバッチ計算を構成する方法です。Map は入力の記録をキーと値のペアに変換し、Shuffle は同じキーの値を集め、Reduce はキーごとの値をまとめて結果にします。WordCount では、キーは商品ラベルで、1回の出現につき値1を与えます。単語集計はデータの流れを見える形にするための小さな例であり、完全な売上分析システムではありません。

用意された学習用ツールで各段階を調べた後、同じ入力に対して、コンパイル済みの実際の Hadoop WordCount プログラムを実行します。学習用ツールは1つのプロセスでアルゴリズムの段階を表示するもので、分散実行の記録ではありません。Hadoop はローカルモードで実行し、計算に集中します。次の実験では、同じアルゴリズムを YARN に移します。Java や Python のプログラミングは必要ありません。

商品ラベルをキーと値のペアにする

このステップでは、入力を確認し、商品の各出現が Map を通る様子をたどります。

ローカルの作業ディレクトリから始めます。

cd /home/labex/project

入力の6行を読みます。

cat /home/labex/project/data/product-words.txt

期待されるテキストは次のとおりです。

notebook pen
notebook folder
pen pen
folder notebook
notebook
folder pen

ここで一度止まり、商品を手で数えてみてください。notebook は4つの行に1回ずつ登場します。pen は、3行目の2回も含め、合計4回です。folder は3回です。プログラムも同じ合計を出すはずです。

キーと値のペアは、ラベルと値を対応させたものです。このアルゴリズムでは、Map は出現するたびに (product, 1) を出力します。pen が2回あれば、入力行につき1ペアではなく、別々の2ペアを作ります。

用意された実行ファイル wordcount-stages は、段階の名前とローカルの入力ファイルを受け取ります。map の表示を使います。

/home/labex/project/tools/wordcount-stages map /home/labex/project/data/product-words.txt

出力は次のように始まります。

notebook 1
pen 1
notebook 1
folder 1

キーと値の間はタブです。商品の出現1つにつき1つ、合計11ペアがあります。このツールはアルゴリズムを見える形にするもので、Hadoop サービスの起動やタスクのスケジュールは行いません。この段階の出力を保存します。

/home/labex/project/tools/wordcount-stages map /home/labex/project/data/product-words.txt > /home/labex/project/output/map-pairs.txt

入力の表現が変わりました。行全体が、商品別にまとめられる個々の加算分になりました。

加算分をグループ化して集計する

このステップでは、ペアが Shuffle と Reduce を通る様子をたどり、出力を手作業の予想と結び付けます。

Map は個別の加算分を出力します。ある商品の合計を求めるには、その商品への加算分を集める必要があります。Shuffle と Sort は、Reducer が特定の商品の値を受け取れるよう、キーごとにペアを整理します。分散ジョブではタスク間のデータ転送を伴う場合がありますが、学習用ツールはグループ化した値を表示するだけです。

グループ化した表示を調べます。

/home/labex/project/tools/wordcount-stages shuffle /home/labex/project/data/product-words.txt

期待されるグループ化後の値は次のとおりです。

folder 1,1,1
notebook 1,1,1,1
pen 1,1,1,1

キーは並べ替えられています。カンマで区切られた各1は、新しい記録ではなく、元の出現を表したままです。グループ化の結果を保存します。

/home/labex/project/tools/wordcount-stages shuffle /home/labex/project/data/product-words.txt > /home/labex/project/output/shuffled-values.txt

Reduce はキーごとの値をまとめます。ここでは整数を足します。別のアプリケーションでは異なるまとめ方もでき、MapReduce は集計だけに限られません。

Reduce の表示を確認します。

/home/labex/project/tools/wordcount-stages reduce /home/labex/project/data/product-words.txt

期待される結果は次のとおりです。

folder 3
notebook 4
pen 4

ステップ1で手で数えた結果と比べてください。次に実際の Hadoop アプリケーションと比較できるよう、結果を保存します。

/home/labex/project/tools/wordcount-stages reduce /home/labex/project/data/product-words.txt > /home/labex/project/output/teaching-counts.txt

元の出現が別々の行にあっても、結果は各キーのすべての出現によって決まります。だからこそ、Map と Reduce の間にグループ化の段階があるのです。

実際の Hadoop WordCount プログラムを実行する

このステップでは、コンパイル済みの Hadoop サンプルで、同じ集計をローカルモードで実行します。

JAR は Java のプログラムコードをまとめたものです。Hadoop とサンプル JAR はインストール済みなので、コードをコンパイルする必要はありません。hadoop jar は指定した JAR を実行し、wordcount はその WordCount サンプルを選びます。以下の2つの -D 設定は、この実行でローカルの実行フレームワークとローカルファイルシステムを選択します。最後の2つの引数は、入力ファイルと新しい出力ディレクトリです。

hadoop jar /opt/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.5.0.jar wordcount -Dmapreduce.framework.name=local -Dfs.defaultFS=file:/// /home/labex/project/data/product-words.txt /home/labex/project/output/local-wordcount

プロンプトが戻るまで待ちます。Hadoop は進行状況と診断メッセージを出力しますが、パス、識別子、所要時間は変わります。これは YARN アプリケーションではなく、ローカルランナーを使った実際の Hadoop プログラムです。この実験では HDFS や YARN のサービスを起動せず、入力と出力は両方ともローカルです。

ローカルの出力ディレクトリを一覧表示します。

ls /home/labex/project/output/local-wordcount

_SUCCESS マーカーと、part-... の結果ファイルがあるはずです。_SUCCESS は出力が正常に完了したことを示し、結果ファイルには確認するデータが入っています。ローカルのシェルの part-* パターンで結果ファイルを選びます。比較を安定させるため、連結した内容を sort に渡します。

cat /home/labex/project/output/local-wordcount/part-* | sort > /home/labex/project/output/hadoop-counts.txt

学習用ツールの集計と比較します。

diff /home/labex/project/output/teaching-counts.txt /home/labex/project/output/hadoop-counts.txt

差分はないはずです。説明で学んだアルゴリズムと、実際のプログラムの出力を結び付けられました。この実行の出力ディレクトリがすでにある場合は、完成したファイルを確認してください。むやみに削除したり、再投入すれば上書きされると考えたりしてはいけません。

後で重要になる違いも押さえましょう。HDFS のブロックはバイト列を保存し、入力スプリットは入力の作業単位を定義し、タスクは作業を実行します。これらは関係していますが、常に一対一に対応するわけではありません。結果ファイルは処理タスクの出力であり、元の保存ブロックのコピーではありません。

入力の変更を1つ予想して試す

このステップでは、作業用コピーに商品を1回追加し、どの結果が変わるか確かめます。

読み取り専用の元の入力は変更しません。ローカルの cat と > で、新しい書き込み可能な作業用コピーを作ります。両方のパスはローカルで、変更するのは出力側のファイルです。

cat /home/labex/project/data/product-words.txt > /home/labex/project/output/changed-product-words.txt

notebook の出現を1つ追記した場合の新しい集計を予想してください。その合計だけが4から5に増えるはずです。printf は指定したテキストを表示し、\n は改行を意味します。>> は既存の内容を置き換えず、出力を末尾に追加します。

printf 'notebook\n' >> /home/labex/project/output/changed-product-words.txt

変更した入力を確認します。

cat /home/labex/project/output/changed-product-words.txt

末尾に新しい1行があるはずです。入力と出力のパスだけを変え、同じ Hadoop プログラムを実行します。

hadoop jar /opt/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.5.0.jar wordcount -Dmapreduce.framework.name=local -Dfs.defaultFS=file:/// /home/labex/project/output/changed-product-words.txt /home/labex/project/output/changed-wordcount

実際の結果を並べ替えて読みます。

cat /home/labex/project/output/changed-wordcount/part-* | sort

期待される結果は次のとおりです。

folder 3
notebook 5
pen 4

新しい Map のペアは notebook グループに入るので、その Reduce の合計だけが変わります。これで、コマンドが成功したことを認識するだけでなく、結果を説明できます。次の実験では、アルゴリズムと集計ルールをそのままに、HDFS が入力を提供し、YARN が実行リソースを管理する構成に進みます。

まとめ

商品の出現を Map のペアに変え、Shuffle で値をグループ化し、Reduce でまとめる流れをたどりました。実際の Hadoop WordCount は手計算と学習用ツールに一致し、入力に出現を1つ加えると、予想どおりに結果が変わりました。

アルゴリズムだけに集中するため、ローカルランナーを使いました。次はアルゴリズムをそのままに、実際の YARN アプリケーションが HDFS の入力を読み、HDFS に結果を出力する仕組みを学びます。