MongoDBのちょっと詳しいチュートリアル<a href="http://b.hatena.ne.jp/entry/http://d.hatena.ne.jp/doryokujin/20101010/1286668402" class="bookmark-count"><img src="http://b.hatena.ne.jp/entry/image/http://d.hatena.ne.jp/doryokujin/20101010/1286668402" title="はてなブックマーク - Mo

@です。本エントリーから数回にわけてMongoDBの紹介をつらつら書いていきたいと思います。日々、MongoDBの魅力にどっぷりな僕でして、それを少しでも多くの方に共有できたらというモチベーションで書いています。今回はチュートリアルとして主要な機能を少し詳しめに紹介していきます。アジェンダは以下の通りです:

  • はじめに
  • ちょっと詳しいチュートリアル
    • オープンソース
    • NoSQL・ドキュメント指向データベース
    • ドライバとして多くの言語サポート
    • 完全なインデックスサポート
    • リッチなクエリー
    • MySQLに類似した機能群
    • レプリケーション機能
    • オートシャーディング
    • 巨大ファイルを扱うGridFS
  • 今後の予定
    • 本家ドキュメントの翻訳
    • より深い機能説明
    • 勉強会での発表
    • Production Deploymentsとして弊社の名前を掲載する

はじめに

僕は現在MongoDBをソーシャルアプリのログ解析の中間集計データの格納・集約場所としてMongoDBを活用させてもらっています。MongoDB採用に至る経緯や使用目的については前々回のエントリースライドをご覧頂ければと思います。現在過去3ヶ月の行動ログや課金データなどの集計データのMongoDBへの格納がほぼ完了したところでして、現在約2億件のレコードが、シャーディングされた3台のマシンで管理しています。今後は実際のデータ解析を行いながら経営や企画の方々にデータの側から様々な提言をしていきたいと思っています。と、同時に誰でも簡単にデータを閲覧できるようなフロント部分の実装(Webアプリ)も進めて行く予定です。MongoDBにはmongooseというJavaScriptからMongoDBにアクセスできる非常に便利なライブラリーがありますので、これとテーブルや描画関連のAjaxを利用しながら効率よく作業を進めていく予定です。相関を求めたりモデル式を作成したりといった少し突っ込んだ解析にはRに全部任せてしまおうと考えています。このように、芸者東京エンターテインメント GTEにおいて、MongoDBはソーシャルアプリのログ解析のバックエンドとフロントエンドをつなぐ非常に重要な役割を担う存在となっています。

f:id:doryokujin:20101010053256p:image
おみせやさんのログ解析

業務でのアプリケーションや実際のパフォーマンスにつきましては次回のデータマイニング+WEB 勉強会@東京 #TokyoWebMiningで発表させて頂きたいと思います。(次回は11月14日予定です。是非ともご参加下さい!アジェンダはこちらです。)

ちょっと詳しいチュートリアル

それではMongoDBとは一体どのようなDBなのか、今回は主に公式ドキュメントを参照しながら少し突っ込んだ形のチュートリアルとてして見ていきたいと思います。参考にした資料、詳しく書かれた資料などは随時リンクを張っていきます。

オープンソース

現在MongoDBは開発・サポート元である10genからオープンソースとして公開されています。彼らによって商用サポートも行われているみたいです。また、素晴らしいことに公式ドキュメントが非常に充実しています。公式ドキュメントの日本語訳もかなり進んでいます。この日本語ドキュメントは全て@さん1人の手によって運営されています。(ブログはこちら)@さんの日本語訳によってたくさんの方がMongoDBを知り、活用する機会が生まれたと思います。本当にありがとうございます。

NoSQL・ドキュメント指向データベース(JSON-like data schemas)

MongoDBは話題のNoSQL(Not Only SQL)の代表として今や多くの記事に取り上げられるようになりました。内部はC++で記述され、SQLと違ってスキーマレスであり、データはBSONと呼ばれるJSONに似た形式で格納されています。ユーザー側では完全なJSON形式として扱えます。弊社のソーシャルアプリ、おみせやさんのログ解析において格納しているユーザー課金データを例にしますと、

{
 "_id" : "2010-06-28+000000+Charge",
 "lastUpdate" : "2010-09-20",
 "userId" : "000000",
 "date" : "2010-06-28",
 "actionType" : "Charge",
 "totalCharge" : 1210,
 "boughtItem" : { "おもちゃの素EX 5個" : 1,
         "おもちゃの素 5個" : 1,
         "ヌイグルミの素 5個" : 1,
         "おすすめ看板" : 1,
         "梅の湯" : 2 }
}

のような形になっています。

ドキュメント指向データベースは他にもCouchDB等が有名です。CouchDBとの機能比較については

に表があってとてもわかりやすいと思います。他にも、

が参考になります。

NoSQLにつきましてはたくさんの情報が公開されていますので各自お調べ頂けると幸いです。ここではとても詳しく各種NoSQLについて述べられたHPを紹介しておきます。比較表などもあってとても素晴らしい資料だと思います。これが無料で公開されていることは驚きです。

ドライバとして多くの言語サポート

◆各種言語向けドライバ

現在MySQLと同じくらい豊富にドライバが用意されています。公式にサポートされている言語は、

C, C++, Java, Javascript, Perl, PHP, Python, Ruby

ですが、コミュニティサポートとして

C# and .NET, Clojure, ColdFusion, Erlang, Go, Groovy, Haskell, Javascript, Lua, Objective C, Scala, etc...

など、非常に充実しています。

Rails

さらにWebアプリケーションフレームワークのModelとしてMongoDBを扱うためのライブラリも用意されています。例えばRuby On Railsここに詳しく書かれていたり、企業のRails+MongoDB活用事例としては、

が参考になると思います。

Python+Django

僕はPythonを利用してMongoDBの操作を行っています。ドライバは2種類、

を確認しています。僕は前者のPymongoを使用しています。後者はまだ試した事がありませんので比較はしていませんが併せてDjango-MongoKitも用意されているので、Djangoまでの使用を考えるとこちらを使う方が一貫性があって良さそうです。その他のDjangoのサポートとして

があるみたいです。僕もDjangoの使用を検討していましたが、まずはmongooseをつかってWebアプリを実装しようと考えています。Python+Djangoに関してはこちらの記事が色々を参考になると思います:

その他のPythonでのMongoDB支援ライブラリとして、

などがあります。

完全なインデックスサポート

スキーマレスでありながら、完全にインデックスをサポートしているのがMongoDBの大きな強みであると思っています。MongoDBのインデックスは、MySQLのようなRDBMSのインデックスと概念的に似てて、MySQLでインデックスを付けたいような場所に、MongoDBでもインデックスを付ける事ができます。

先ほど例で示したおみせやさんの課金データに対しては、キーである、"_id" ・ "lastUpdate"・"userId"・"date"・"actionType"・"totalCharge"・"boughtItem"
の全てに対してインデックスを付けることが可能です。例えば"userId"に対してインデックスを作成する場合、以下のようなコマンドを指定します。

db.things.ensureIndex({"userId": 1})

スキーマレスですので、例えば"userId"が存在しないレコードがあるかもしれませんが、その場合は値がnullであるものとしてインデックスが付けらることになります。数字の1(-1)は昇順か降順を指定します。

さらにキー"boughtItem"の(連想)配列の要素に対してもインデックスを貼ることができ、

db.things.ensureIndex({"boughtItem.梅の湯": 1})

とすれば良く、"boughtItem"に"梅の湯"を含むレコードを高速に検索・ソートすることが可能になります。

他にも複合インデックスなど、非常に柔軟にインデックスを利用することができます。日本語ドキュメントに詳しく書かれていますのでぜひ一読してみてください。また、インデックスの情報は全てメモリ上に保存されることになりますので、大量レコードのDBに対して複数のインデックスを用いる場合は、十分なメモリ容量が必要になりますので注意して下さい。

リッチなクエリー

◆柔軟な検索条件指定

MongoDBにはダイナミックなクエリーのサポートがあります。様々な条件で柔軟な検索が行えます。HBaseやCassandraといった他のNoSQLでは不可能な検索条件もMongoDBでは可能です。こちらに様々な条件での検索方法が記載されています。例えば、

  • 範囲指定
  • 存在指定
  • 配列のサイズ指定
  • 正規表現

といった条件で検索可能になります。

◆集約関数

また、MySQLの "COUNT" や "DISTINCT"、"GROUP BY" といった集約関数も利用することができます。さらに大量レコードに対する集約関数の適用の際には"Map/Reduce"を使って、シャーディングされた複数のマシンのパワーを利用して処理を行う事ができます。詳しくはクエリーに関しては日本語ドキュメントのクエリー項目の、高度なクエリー"集約関数"を参照して下さい。

そして、以下のMySQLとMongoDBのクエリー比較表が面白いと思います。

MySQLに類似した機能群

今までの機能を見て頂けた通り、MongoDBはNoSQLでありながら非常にSQLに近い機能を持っていることが伺えます。こちらのチャートSQLとMongoDBのコマンド比較が載っています。

さらに現在ではMySQLのデータをそのままMongoDBへ移行した事例が多くの企業から報告されています。

レプリケーション機能

MongoDBにはMySQLと同じくレプリケーション機能を備えており、かつ非常に簡単な設定でそれを実現することができます。現在レプリケーションに関する機能として3種類用意されております。各々を簡単に説明していきます。ここではいきなりmongodコマンドを紹介していますが、これだけで簡単にレプリケーションが行えるという事を理解してもらいと思ったからです。

  1. Master/Slave
  2. Replica Pairs
  3. Replica Sets

◆Master/Slave

あるMasterに対して、そのSlaveを構成する設定です。MasterとSlaveは基本的には入れ替わることはありません。まず、Masterにしたいサーバーに対して、MongoDBのプロセス起動時に

$ bin/mongod --master [--dbpath /data/masterdb/]

として起動します。--dbpathは明示的にデータの格納場所を指定するオプションです。Slave側ではこのMasterの場所を指定すればよく、

$ bin/mongod --slave --source [:] [--dbpath /data/slavedb/]

としてやれば自動的にMasterと同期を行ってくれます。その他のオプションとして同期間隔やMaster/Slave間のやりとりを記録するoplogのサイズを指定することができます。このoplogをより扱いやすくするライブラリも存在します。

さらに2つのMasterに対して1つのSlaveを構成するといったことも可能です。

◆Replica Pairs

Replica Pairsは状況に応じて、MasterとSlaveが自動的に入れ替える事ができるものです。例えばMaster側に障害があった場合には自動的にSlaveをMasterに切り替えて運用を続けることができます。さらにSlaveがMasterに切り替わった場合、新しいSlaveとして使用するサーバーもあらかじめ設定しておくことができます。こちらも設定は非常に簡単で、それぞれのサーバーで

$ ./mongod --pairwith --arbiter

としてプロセスを起動するだけです。remoteserver は、pariの相手のサーバのホスト名です。 標準的でないポートで動かしたい場合には、 :port を付けてください。arbiterserverはある時点で、どのMongoデータベースがMasterとなるかを決定するのを手伝うMongoのデータベースサーバで、独立なサーバーとして起動します。

◆Replica Sets

Replica setsは"Replica Pairs version 2" として最新のバージョン1.6から導入された先進的な機能です。3台以上のサーバーのレプリケーションを行い、障害時には自動でMasterを切り替えることができます。こちらも設定方法はそれほど難しくありませんので、
公式ドキュメントSetting up replica sets with MongoDB 1.6、またはpdf資料や下のスライドを参考してみてください。

オートシャーディング

MongoDBはCassandraやHBaseと同様に容易にスケールアウトさせる事が可能で、また複数台のマシンにDBを分割して配置することができます。これによって1台のマシンの負荷を低減させたり、ディスク容量も低減させることができます。MongoDBはこのDB分割自身を自動で行ってくれる機能を備えています。例えばユーザーIDが1000番台のレコードはこのサーバーに、2000番台は…みたいなことをこちらが考える必要がありません。(もちろんマニュアルに指定することもできます。)Auto Partithiningといわれるこの機能はYahooの[http://research.yahoo.com/project/212:title=PNUTS:やGoogleのBigTableと同じ思想で設計されています。

また、どれかのマシンがダウンしてしまった時も、そのマシンが担っていたデータは自動的に他のマシンに振り分けてくれます。Dynamoと同じConsistent Hashingを採用しています。ConsistentHashingについてはこちらがわかりやすいです

またクエリーの所で紹介しましたが、シャーディングした複数のマシンパワーを利用してMapReduceを行うこともできます。

参考資料は本家ドキュメントと、以下の資料がわかりやすいと思います。

巨大ファイルを扱うGridFS

MongoDBは、バイナリデータの格納をBSONでサポートしています。しかし、MondoDBでのBSONオブジェクトは4MBのサイズに制限されているために、通常ではそれ以上のデータを格納することは不可能です。GridFSの仕様は、それ以上の大きいファイルを複数のドキュメントに透過的に分割する方法を提供します。これは、巨大なファイルを効率的に格納したり、ビデオなどの巨大なファイルを様々な方法で(例えばファイルの最初のNバイトを取得とか)効率的に扱うことができます。

今後の予定

さて、MongoDBに関しては今後とも積極的に情報を書いて行けたらと思っています。

より深い機能説明

今回はチュートリアルとして駆け足で紹介しました。今後はもっと内容を掘り下げて行きたいと思います。例えばシャーディングやレプリケーションに機能ついての詳細説明や、メモリーの消費量や32bitOSでの制約などを紹介していければと思っています。まずはMongoDBの特徴について今回よりもより詳しく書かれたNotes on MongoDBという記事を翻訳してみたいと思っています。

本家ドキュメントの翻訳

現在の日本語ドキュメントを作成していただいている@さんに協力する形で、今後僕も公式ドキュメントのAdmin Zoneの部分の残りの訳を担当させて頂くことになりました。現在のような質の高いドキュメントを維持できるかは不安ですが、皆さんのお役に立てる事を信じて、しっかりと訳を進めていきたいと思います。進捗はブログやTwitter等で告知していきたいと思っています。

勉強会での発表

今後とも勉強会には積極的に参加して、発表を通じて多くの方と議論をしていければと思っています。こちらは主に、現在仕事で行っている、「ソーシャルアプリのログ解析での(アプリケーションとしての)MongoDB」という位置づけで、前回に引き続き、リアルな話をしていきたいと思っています。

Production Deploymentsとして弊社の名前を掲載する

これは実は自分の中で大きな目標にしているのですが、MongoDBの公式サイトのProduction Deploymentsにソーシャルアプリでの導入事例として日本企業初として芸者東京エンターテインメントの名前を是非とも連ねたいですね…はい。

また、このサイトは他の企業がどのような用途でMongoDBを導入しているかという、非常に参考になる部分が多いページですので、ぜひともこのサイトの翻訳と各社の事例紹介を行っていきたいと思っています。