mongodbでMapReduceをして遊ぶ

mongodbは最初からMapReduceをサポートしていて、巨大なデータを効率的に計算することができます。

今回はmongodbにあるデータを種類ごとにカウントするという問題に対してMapReduceで計算して遊びたいと思います。

MapReduce

以下のようなmouse,monkey、dogの3種類のどれかが入り混じっているデータを考えましょう。

mouse,monkey,dogのそれぞれが何個ずつDataの中に入っているかをカウントアップしたいとします。

普通にプログラムを書くとすれば、上から逐次検索をし、mouseならmouse_count変数を+1、monkeyならmonkey_count変数を+1などのようにカウントアップしていくと思います。

しかし、もしDataが超巨大なデータ郡だった場合、以下のような問題が考えられます。

  • 逐次検索なので並列処理ができない
  • 途中で計算機が壊れた場合、1から再計算しなければならない
  • カウントアップする変数が多くなりすぎてメモリが足りなくなる

上記のような問題があるため、ビッグデータの場合、メモリやCPUの効率よく使用して計算する必要があります。

それを可能にするのがMapReduceとなります。

MapReduceではMap処理とReduce処理の2つの処理から構成されます。

Map処理では処理対象のデータに対し、計算したい処理がお互いに依存しない単位にデータを分割します。

例えば今回の場合は、mouseやmonkeyの”数をカウントする”ことが計算したい処理なので、mouseはmouseだけのデータ、monkeyはmonkeyだけのデータに分割すればお互いに依存が無くなりそうです。mouseをカウントしたいならmouseのデータだけあればいいのです。

こうしてMap処理によって生成されたデータはお互いに処理の依存関係がなくなり、処理の並列化が可能となります。

また、mouseのカウントアップの計算に失敗した場合、再計算する必要があるのはmouseのデータのみとなり、計算失敗した場合のリスクオフにもなります。

Reduce処理では分割されたそれぞれのデータに対し、計算したかった処理を計算します。

今回の場合、カウントアップがしたいので単純に分割されたデータをカウントアップします。

そうして最終的に1つの計算結果へと集約されます。

このようにMapReduceではデータをお互いに依存しない形にしてから計算することで処理の並列化やメモリ使用の効率化を可能とします。

Mapによって分割されたデータをそれぞれ別のコンピュータ(クラスタ)へと渡し、それぞれのコンピュータで計算された結果を1つのコンピュータでReduceすれば並列コンピューティングをすることができます。

データを用意する

では実際にmongodbを利用してMapReduceをしてみます。

今回はmongodbに以下のようなデータを用意します。

typeがmouseかmonkeyかdogのデータとなります。

このtypeカラムの種類ごとにMapしてカウントアップをしましょう。

mongodbでMapReduceを行うには、collectionのmapReduce関数を使います。

mapReduce関数にはmap関数とreduce関数を与え、第三引数にはどのコレクションに出力したいかを記述します。

map関数ではemit関数をよびだします。emit関数は第一引数に分割するキーを指定します。

このキーが同じ値は処理が依存するとして同じところにMapされます。

今回の場合はtypeごとにまとめたいのでキーをthis.typeとします。

emit関数の第二引数は計算処理に必要な値を渡します。今回はカウントアップをしたいのでデータ1つのカウントを1として渡します。

 

reduce関数では集計処理を行います。

reduce関数はmap関数によってMapされたキーごとに呼びだされ、同一のキーのデータはすべてvalues引数にまとめられて呼び出されます。

今回はカウントアップなのでvaluesのcountの数を数え上げましょう。

最終的に計算結果をreturnしてreduce関数は終了です。

計算結果は以下のようになります。ちゃんとtypeごとにカウントされています。