php と mysql でカジュアルに mapreduce する (short version)

Post on 13-Jan-2015

9.612 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

DESCRIPTION

以下のスライドを再編集・一部加筆した Short Version ですhttp://www.slideshare.net/taketyan/php-mysql-mapreduce

TRANSCRIPT

PHP と MySQL でカジュアルに

MapReduce する(Short Version)

@yuya_takeyama

ショートバージョンでお送りします

• 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

• PHP と MySQL でカジュアルに MapReduce する(スライド・Long Version)http://www.slideshare.net/taketyan/php-mysql-mapreduce

お断り

ビッグデータの話はありません

問い

MongoDB にあってMySQL にないもの

なーんだ?

構造化データ?自動シャーディング?自動フェイルオーバ?

MapReduce!

MapReduce!それもカジュアルな!!

MongoDB はJavaScript で

カジュアルにMapReduce できる

MySQL でもMapReduce

したい!!!

MySQL でもMapReduce

したい!!!

それもカジュアルに!!

モチベーション•プログラミングモデルとしてのMapReduce を使いたい

•GROUP BY では難しい集計•MySQL を入出力にしたい

•LL でサクッとやりたい

モチベーション•プログラミングモデルとしてのMapReduce を使いたい

•GROUP BY では難しい集計•MySQL を入出力にしたい

•LL でサクッとやりたいPHP である必要はあまり無い

MapReduce とは(word count による例)

MapReduce とは

データ処理のためのプログラミングモデル

処理の流れ入力↓

Map↓

Shuffle↓

Reduce↓出力

Map

•入力データを受け取り•複数の Key/Value ペアを出力

•to be or not to be

•<"to", 1>

•<"be", 1>

•<"or", 1>

•<"not", 1>

•<"to", 1>

•<"be", 1>

Shuffle

•Map による Key/Value を

•Key ごとにまとめて出力

•<"to", 1>

•<"be", 1>

•<"or", 1>

•<"not", 1>

•<"to", 1>

•<"be", 1>

•<"be", [1, 1]>

•<"not", [1]>

•<"or", [1]>

•<"to", [1, 1]>

Reduce

•Shuffle による中間データを•集約して答えを出力

•<"be", [1, 1]>

•<"not", [1]>

•<"or", [1]>

•<"to", [1, 1]>

•<"be", 2>

•<"not", 1>

•<"or", 1>

•<"to", 2>

複数の関数の入出力を経て最終的な答えを出力

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

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`

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

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 になる

今後の目標

•非同期 INSERT による並列化•Hadoop へのシームレスな移行方法の提供

今後の野望

•V8 エンジンとかで•ストレージエンジン API を•カジュアルに叩いて•MapReduce したい

ご清聴ありがとうございました

top related