php と mysql でカジュアルに mapreduce する
DESCRIPTION
TRANSCRIPT
PHP と MySQL でカジュアルに
MapReduce する
@yuya_takeyama
アジェンダ
•MapReduce とは
•自作フレームワークMyMR の紹介
お断り (1)
ビッグデータの話はありません
お断り (2)
業務ではまだやってません
お断り (3)
Hadoop未経験です※MongoDB での
MapReduce ならやりました
MapReduce とは
固有名詞として
•Google の大規模データ処理フレームワーク
•検索インデックスの作成とかに使われている
普通名詞として•Map/Reduce 関数でデータを処理するプログラミングモデル
•マシンを増やしただけスケール•Hadoop, MongoDB, CouchDB などが主な実装
処理の流れ 入力↓
Map↓
Reduce↓出力
処理の流れ入力↓
Map↓
Shuffle↓
Reduce↓出力
やや厳密な
より厳密にはもっと複雑らしいです
Map
•入力データを受け取り•複数の Key/Value ペアを出力
Shuffle
•Map による Key/Value を
•Key ごとにまとめて出力
Reduce
•Shuffle による中間データを•集約して答えを出力
複数の関数の入出力を経て最終的な答えを出力
文章中の単語の数を数える例(word count)
入力
•to be or not to be
Map •<"to", 1>
•<"be", 1>
•<"or", 1>
•<"not", 1>
•<"to", 1>
•<"be", 1>
Shuffle•<"be", [1, 1]>
•<"not", [1]>
•<"or", [1]>
•<"to", [1, 1]>
Reduce•<"be", 2>
•<"not", 1>
•<"or", 1>
•<"to", 2>
MapReduce の利点•Map も Reduce も並列化すればスケールする
•関数型っぽい考え方が活きる※ただし, Hadoop や MongoDB の MapReduce の Map と Reduce は 関数型言語のそれとはやや異なる (参照透過でなかったり)
•パターンとして共有しやすい※手続き型のバッチ処理と比較して
MongoDB について
•通常は MapReduce を並列に実行することができない
•それでも MapReduce は便利
•何故か?
スケーラビリティだけじゃない
•プログラミングモデルとしてのMapReduce にも価値がある
•MongoDB で処理が完結
•JS で関数ふたつ書くだけ
MySQL でもMapReduce
したい!!!
というわけで作りました
MyMR
•MySQL を入出力とする
•PHP で Map/Reduce を書く
•コマンドラインで実行
https://github.com/yuya-takeyama/mymr
MyMR による処理の流れ•テーブルからレコードを読む•1 行 1 行に Map (PHP) を適用して中間テーブルへ
•MySQL による Shuffle
•その結果に Reduce (PHP) を適用して出力テーブルへ
文章中の単語の数を数える例(word count)
MyMR による
use \MyMR\Builder;
$builder = new Builder;
$builder->setInputTable('root@localhost/db/texts');$builder->setOutputTable('root@localhost/db/word_counts');
$builder->setMapper(function ($record, $emitter) { $words = preg_split('/\s+/u', $record['text']); foreach ($words as $word) { $emitter->emit($word, 1); }});
$builder->setReducer(function ($key, $values) { $sum = 0; foreach ($values as $count) { $sum += $count; } return array('count' => $sum);});
return $builder;
Map/Reduce の定義
use \MyMR\Builder;
$builder = new Builder;
$builder->setInputTable('root@localhost/db/texts');$builder->setOutputTable('root@localhost/db/word_counts');
$builder->setMapper(function ($record, $emitter) { $words = preg_split('/\s+/u', $record['text']); foreach ($words as $word) { $emitter->emit($word, 1); }});
$builder->setReducer(function ($key, $values) { $sum = 0; foreach ($values as $count) { $sum += $count; } return array('count' => $sum);});
return $builder;
Map/Reduce の定義
入出力テーブルの指定
use \MyMR\Builder;
$builder = new Builder;
$builder->setInputTable('root@localhost/db/texts');$builder->setOutputTable('root@localhost/db/word_counts');
$builder->setMapper(function ($record, $emitter) { $words = preg_split('/\s+/u', $record['text']); foreach ($words as $word) { $emitter->emit($word, 1); }});
$builder->setReducer(function ($key, $values) { $sum = 0; foreach ($values as $count) { $sum += $count; } return array('count' => $sum);});
return $builder;
Map/Reduce の定義
この辺が Map
use \MyMR\Builder;
$builder = new Builder;
$builder->setInputTable('root@localhost/db/texts');$builder->setOutputTable('root@localhost/db/word_counts');
$builder->setMapper(function ($record, $emitter) { $words = preg_split('/\s+/u', $record['text']); foreach ($words as $word) { $emitter->emit($word, 1); }});
$builder->setReducer(function ($key, $values) { $sum = 0; foreach ($values as $count) { $sum += $count; } return array('count' => $sum);});
return $builder;
Map/Reduce の定義
この辺が Reduce
入力
•to be or not to be
function ($record, $emitter) { $words = preg_split('/\s+/u', $record['text']); foreach ($words as $word) { $emitter->emit($word, 1); }}
Map
function ($record, $emitter) { $words = preg_split('/\s+/u', $record['text']); foreach ($words as $word) { $emitter->emit($word, 1); }}
レコードを連想配列として受け取る
Map
function ($record, $emitter) { $words = preg_split('/\s+/u', $record['text']); foreach ($words as $word) { $emitter->emit($word, 1); }} text カラム内の
文字列をスペースで分割
Map
function ($record, $emitter) { $words = preg_split('/\s+/u', $record['text']); foreach ($words as $word) { $emitter->emit($word, 1); }}
Key/Value のペアとして中間テーブルに INSERT
Map
function ($key, $values) { $sum = 0; foreach ($values as $count) { $sum += $count; } return array('count' => $sum);}
Reduce
function ($key, $values) { $sum = 0; foreach ($values as $count) { $sum += $count; } return array('count' => $sum);}
ReduceKey Value の配列
function ($key, $values) { $sum = 0; foreach ($values as $count) { $sum += $count; } return array('count' => $sum);}
ReduceValue を全て足す
function ($key, $values) { $sum = 0; foreach ($values as $count) { $sum += $count; } return array('count' => $sum);}
Reduce
返り値の連想配列をレコードとして INSERT
Map+----+--------------------+| id | text |+----+--------------------+| 1 | to be or not to be |+----+--------------------+
↓ レコードを連想配列として Map へ ↓+----+---------+-------+ | id | key | value | +----+---------+-------+ | 1 | to | 1 | | 2 | be | 1 | | 3 | or | 1 | | 4 | not | 1 | | 5 | to | 1 | | 6 | be | 1 | +----+---------+-------+
Map+----+--------------------+| id | text |+----+--------------------+| 1 | to be or not to be |+----+--------------------+
↓ レコードを連想配列として Map へ ↓+----+---------+-------+ | id | key | value | +----+---------+-------+ | 1 | to | 1 | | 2 | be | 1 | | 3 | or | 1 | | 4 | not | 1 | | 5 | to | 1 | | 6 | be | 1 | +----+---------+-------+
value には JSON で入れるので構造化データも使用可能
Shuffle
↓ キーで GROUP BY して ↓↓ 値は GROUP_CONCAT ↓
+---------+--------+| key | values |+---------+--------+| be | 1,1 || not | 1 || or | 1 || to | 1,1 |+---------+--------+
+----+---------+-------+ | id | key | value | +----+---------+-------+ | 1 | to | 1 | | 2 | be | 1 | | 3 | or | 1 | | 4 | not | 1 | | 5 | to | 1 | | 6 | be | 1 | +----+---------+-------+
SELECT `key`, GROUP_CONCAT(`value`)FROM `中間テーブル`
GROUP BY `key`
Reduce
↓ キーと値の配列を Reduce へ ↓
+---------+--------+| key | values |+---------+--------+| be | 1,1 || not | 1 || or | 1 || to | 1,1 |+---------+--------+
+----+---------+-------+| id | key | count |+----+---------+-------+| 1 | be | 2 || 2 | not | 1 || 3 | or | 1 || 4 | to | 2 |+----+---------+-------+
Reduce
↓ キーと値の配列を Reduce へ ↓
+---------+--------+| key | values |+---------+--------+| be | 1,1 || not | 1 || or | 1 || to | 1,1 |+---------+--------+
+----+---------+-------+| id | key | count |+----+---------+-------+| 1 | be | 2 || 2 | not | 1 || 3 | or | 1 || 4 | to | 2 |+----+---------+-------+
実際にはデリミタとして改行を使用改行区切りの JSON になる
モチベーション
•プログラミングモデルとしてのMapReduce を使いたい
•MySQL を入出力にしたい
•LL でサクッとやりたい
モチベーション
•プログラミングモデルとしてのMapReduce を使いたい
•MySQL を入出力にしたい
•LL でサクッとやりたいPHP である必要はあまり無い
今後の目標
•非同期 INSERT による並列化•Hadoop へのシームレスな移行方法の提供
まとめ
•ビッグデータは無くともMapReduce は有効
•MySQL でできたら便利なはず
•PHP で書けたら楽しいはず
リンク
• MyMR on GitHubhttps://github.com/yuya-takeyama/mymr
• PHP と MySQL でカジュアルに MapReduce するhttp://blog.yuyat.jp/archives/1706
• もっとカジュアルに PHP と MySQL で MapReduce するhttp://blog.yuyat.jp/archives/1853
ご清聴ありがとうございました