ビッグデータ向けプラットフォームに入門する - Hadoop


こちらのページは[ビッグデータ向けプラットフォームに入門する](https://qiita.com/mito_110/items/0a275ecbddb9fd678ed1)の子記事です。

prev: [ビッグデータ向けプラットフォームに入門する - DWH](https://qiita.com/mito_110/items/fa224895d1e583bbffee)
next: [ビッグデータ向けプラットフォームに入門する - NoSQL](https://qiita.com/mito_110/items/d76a92f6b78e62451a21)

---

DWHはソフト・ハード一体型であるため、容量あたりのコストが高くつき、誰でも気軽に使えるものではありません。
しかしHadoopを用いることで、MPPのDWHのような大容量のデータの保持、
および集計処理を汎用的なサーバ(コモディティサーバ)上で実行することが可能です。

Hadoopは、

- **HDFS(Hadoop Distributed File System)によるデータ管理**
- **Map/Reduceフレームワークによる高速な集計処理**

によりこれを実現しています。

### アーキテクチャ
HDFS, Map/Reduceについて触れる前にHadoopのざっくりとしたアーキテクチャに触れましょう。
Hadoopクラスタは以下のようなMaster-Slave構成となっています。

![Hadoop_architecture.png](https://qiita-image-store.s3.amazonaws.com/0/303946/6fdf9d05-f8ea-1161-829b-2951e0eb98d5.png)

実際のデータはDataNode上に分散配置されます。
NameNodeはどのデータがどこにあるかの管理、NameNodeの死活監視、HDFS操作の受付などを行います。

またJobTrackerではクライアントから渡されたJobを細かなTaskに分割して
TaskTrackerに分配し、実際の処理はTaskTracker上で実行されます。
これによりデータと実際の処理を行うプログラムの距離が近い、
すなわち処理中にネットワーク通信が発生しないため、高速に処理を実行することが可能となっています。

一般的にmasterはJobTrackerとNameNodeは1台ずつ(HA構成をとることはできます)、
slaveはTaskTrackerとDataNodeが同居し、数十台から数千台規模で構成されます。

### HDFS
大量のクラスタに分散して配置されるデータを統括的に扱うためのファイルシステムがHDFSです。
HDFSはext4, xfs等の通常のファイルシステム上に作られるファイルシステムであり、
クライアント側からはどこのNameNodeにどのデータがあるかを意識せずに使うことができます。

HDFSには以下のような特徴があります。

- **1つのファイルは複数の「ブロック」に分割される**
  - ブロックサイズは汎用的なファイルシステムよりかなり大きい(デフォルトで128MB)
  - ブロック単位でDiskI/Oを実行することで一度に大量のデータをまとめて扱うことが可能
  - ブロック単位でのレプリケーションを取れる
- **集計に特化したアクセスパターンの限定**
  - シーケンシャルなストリーム読み込みを前提とし、ランダムアクセス処理には向かない
  - 書き込みは1度だけ行い、データの更新を行わない(write-once-read-many)
- **ノードをあとから追加することで容易に容量を増やすことが可能**

HDFSの操作はNameNodeで受付を行い、NameNodeは対象となるブロックを持つDataNodeのリストを
クライアントに返却します。
その後クライアントはDataNodeと直接やり取りを行うことで、ネットワーク通信を極力減らしています。

### Map/Reduce
Map/Reduceプログラミングモデルを用いることで、分散配置されたデータに対し
高速に集計処理を行うことができます。

Map/Reduceはmap, shuffle, reduceの3段階に分けられ、以下のような流れで処理が行われます。
まず各TaskTracker(DataNode)においてmap処理が行われます。map処理では基本的に各レコードに対してフィルタリングを行い、Key-Value形式で出力します。
shuffle処理ではmap処理の出力結果を集約し、ソートします。
なお、shuffle処理はユーザーが処理内容を指定せずに自動的に実行されます。
shuffle処理の出力結果はKeyごとにTaskTrackerに分配され、集計用のReduce処理が走ります。
これらは高階関数としてのmap関数やreduce関数をイメージすると分かりやすいかもしれません。

これは非常に単純化すると、「**分散して保持されているデータごとに、各担当がそれぞれデータに対して処理を行い、その結果をまとめる**」ということになります。
イメージだけでは分かりにくいので、単純な例を考えてみます。

ショッピングサイトで、アクセスログをもとに「ユーザーがどのページから商品001のページに遷移してきたかをカウントする」処理を考えてみましょう。

![データ処理プラットフォーム入門_素材.002.png](https://qiita-image-store.s3.amazonaws.com/0/303946/5452ae5d-55e7-2d33-17be-adf526414e49.png)

map処理ではまず各TaskTracker(DataNode)上に配置されたデータに対し、商品ID=001における参照元のURLを抽出し、Key-Value形式で出力します。
今回は単純に参照元のURLをkeyとしその回数をvalueとして与えています。

次にshuffle処理ではこの出力結果に対し、keyごとのvalueをまとめ、keyごとにソートするという処理が行われます。この際TaskTracker間で通信を行い、データの転送が行われます。

最後にreduce処理が実行されます。keyごとにまとめられたvalueに対し予め定義した処理が実行されます。
今回はvalueとして与えられた配列の値の合計を出力する処理を行うことで、最終的な参照元に対する集計が行えたことが分かります。

この例では単純に参照元のページのみを集計しましたが、
例えばどういった検索クエリでたどり着いたかを集計したり、不正なアクセスを弾いたり、ユニークユーザーのみをカウントしたりする処理についても複数のMap/Reduce処理を組み合わせることで実現できます。

このようにMap/Reduceは仕組みは単純である一方、Map処理、Reduce処理の設計・組み合わせ次第で
どのような集計処理も柔軟に実現できる、非常に応用が効くプログラミングモデルであるといえます。

また、**Map/Reduce処理の大きな特徴として、それぞれの処理の並列性が高いことが挙げられます**。
再度例に挙げた処理を見直すと各TaskTracker間で通信がが必要となるのはmap->shuffle, shuffle->reduceなど各プロセスが切り替わる時のみであることがわかるかと思います。
これが、Map/Reduceが高い処理性能を持てる一つの要因となっています。

### Hadoopの特徴
Hadoopは先に挙げたHDFS、Map/Reduceを用いることでコモディティサーバでもデータを分散配置し、効率的に集計を行うことが可能としています。

また、Hadoopでは関連するプロダクトが充実しており、Map/Reduce処理を自分で設計せずにSQLライクなクエリで集計が行えるHive, リソース管理を行うYarnをはじめとし、アドホックな集計処理に特化したPresto, Impala、Map/Reduceより効率的に集計を行うTezなどが挙げられます。

さらに、Hadoopは結局データはただのファイルとして保持されるため、**データ形式を柔軟に定義することができます**。例えばParquetを用いれば列指向形式で保存することも可能です。

このように述べるとHadoopではなんでもできるようにも思えますが、もちろん不得手な部分もあります。
一つのレコードを取ってくるような場合レスポンスタイムは非常に長く、またDWHを含めたRDBと比較しても、ACID特性がなくデータの厳密性の面では劣ります。

さらにDWHのようなソフトとハードが一体となったような設計のプロダクトと比較すると、
集計時間の面でもやや劣ります。

それでも**データの保持、集計においては汎用性の高いプロダクトであることには異論の余地はない**と言えます。