MongoDBをメッセージキューとしての利用方法を紹介したいと思います。
拙作「ふぁぼるっく」では、TwitterのREST API呼び出しによるクロールのほか、UserStreamに接続し大量(現在約7000アカウント)の利用者からのふぁぼやRT情報を収集しているため、人気のツイートが現れると突発的に処理量が増えます。
メインのデータベースとしてPostgreSQLを採用していますが、処理量増大時にデータベースでは接続本数が足りなくなり、また接続~レコードの存在確認~レコード挿入までに時間が掛かるため、他の挿入処理まで待っているとUserStreamは情報がどんどん流れてくるために処理しきれないという事態が発生してしまいます。
そこで、とりあえず情報を貯めておくキューを用意することになり、ふぁぼるっくでは、ここでMongoDBを採用しています。
MongoDBに至った理由としては、以下の点があります。
・大量接続が可能
→PostgreSQLだとサーバーリソース的に1000接続ぐらいが限界なので、同時に20000接続まで可能なMongoDBは魅力的。実際にメモリ2GBのVPS上で運用していますが、20000接続までできました。
・接続から挿入までが速い
→書き込み完了前に処理が返ってきます。失敗する可能性もありますが、正常に動いている限りは失敗する可能性が少ないです。要件的には、失敗しても再取得可能なため無視できます。
・重複していても上書きできる
→MySQLで言うREPLACE INTOができます。正確に言うと、重複したキーを持つデータを保存したら常に上書きです。これは複数のUserStreamから同じデータが投入される場合があるのですが、一般的なメッセージキューとは少し違った要件があり、プライマリキーとなる部分が同じものの場合は1件として扱いたいためです。
・複数のワーカーから取得しても重複して取得しない
→キューとして扱うので、ワーカーで取得した情報は、他のワーカーから見えて欲しくない。そこでfindAndModifyコマンド。
_人人人人人人人_
> findAndModify <
 ̄Y^Y^Y^Y^Y^Y^Y ̄
通常、MongoDBには、insert、update、find、removeなどのコマンドでコレクションにアクセスします。
findAndModifyコマンドは、
{ query: ドキュメント, sort: ドキュメント, remove: 真偽値, update: ドキュメント, new: 真偽値, fields: ドキュメント, upsert: 真偽値 }というコマンド書式をしており、findコマンドと同じようにqueryパラメータで指定したオブジェクトが取得できる一方、updateパラメータで指定した値で更新したり、removeパラメータの指定でオブジェクトの削除が行えます。
これら一連の処理はアトミックな処理となっており、オブジェクトの取得と更新がまとめて行われます。
例えば、testコレクションにこのようなデータが入っていたとします。
> db.test.find(); { "_id" : 1, "name" : "takashi" } { "_id" : 2, "name" : "hanako" }ここで
> db.test.findAndModify({query:{"_id":1},update:{"name":"ten_p"}}); { "_id" : 1, "name" : "takashi" }を実行すると、_id:1のデータが取得できました。
内容を見てみると
> db.test.find(); { "_id" : 1, "name" : "ten_p" } { "_id" : 2, "name" : "hanako" }updateで指定した内容に置き換わっています。
> db.test.findAndModify({query:{"_id":1},remove:true}); { "_id" : 1, "name" : "ten_p" }removeを指定すると、データを取得しつつ
> db.test.find(); { "_id" : 2, "name" : "hanako" }そのデータが削除されます。
ふぁぼるっくでは、この仕組みを利用して、複数のワーカーが並列してMongoDB上のキューからデータベースへの更新処理を行っています。
以上、MongoDBを使うとこんなこともできるよ!という紹介でした。
ふぁぼるっくではTwitterをお使いの方のふぁぼ情報やふぁぼられ情報を集計しています。
ぜひ登録してみてください!