なぜMongoDBなのか

25th Feb, 2010 | mongodb ruby rails

ここを見てもらってる人に、「MongoDBって何がいいの?」と改めて聞かれてしまって、ああ、そっか、そういうこと書いてなかったな、と思ったので、なぜ自分がMongoDBに興味を持っているのか、ということを書いてみた。いざ自分の思いを書いてみたらRails中心の話になってしまったけど、モダンなフレームワークならそんなに話は変わらないのかな、と思っている。


そもそものきっかけは、ここ半年間くらいRuby on Rails(以下RoR)で開発していることにある。

ここ半年弱ほどRoRで開発をして、それなりに満足しているのだけど、ActiveRecordに関しては色々とひっかかるところがあった。

「ActiveRecordがRoRの素晴らしいところそのものだ」と評価している人もいるが、自分の中では逆で、ActiveRecordはRoRの中でもかなりいまいちな部分。

いや、ActiveRecordというよりも、O/Rマッパーの限界なのかな。実際、ActiveRecordはO/Rマッパーの中では、いい方だと思う。今まで、自分用O/Rマッパーを作ったり、それなりの数のO/Rマッパーを見てきたけど、そういうのに比べても、確かにActiveRecordはよくできている。

でも、RoRがよくできたフレームワークなだけに、O/Rマッパー経由RDBMSを使うことに、色々な無駄を感じてしまう。

たとえば、1つのテーブルから同じ条件で検索し、複数のインスタンスを作るために、primary key(id)を検索条件として、何度も検索するとか。「そこはSQL一発で持ってこいよ」と思ってしまう。またそこからlazyに外部キーで他のModel(テーブル)のオブジェクトを持ってこようとすると、またprimary keyでの検索が走る。

一人のユーザが一つの画面を開くだけでSQLを何十回も発行することも普通に起こる。これはどう考えてもSQLの使い方として間違っている。SQLはそういう風に作られていないので、結局(RoR自体を含めた)アプリケーション側の別のレイヤーでインスタンスをCacheしたりと、どんどん複雑になっていく。

そういうのが嫌で、RDBMSとして正しく使おうと、find()で:includeとか:joinsとか使いだすと、今度はどんどんRoR(Ruby)のコードのSQL成分が増していく。全然楽しくない。

つまり、

  1. SQLっぽく書かずに、Ruby(ActiveRecord)っぽくきれいに書くように頑張る
  2. でも、ロジック的/パフォーマンス的に頑張るのが限界だった場合に、SQLっぽいコードをRubyに書くことになる
  3. そうこうしているうちにRubyらしい可読性の高いコードは消えSQLっぽいコードにまみれる

この流れにうんざりする。そもそも1.の段階でも脳内では普通にSQLのことを考えている。だったら最初からSQL書けよ、と思ってしまう。

結局、O/Rマッパーで、きれいに書けるところは、検索した結果でオブジェクトを作る、または、primary *key*指定で持ってきてそれを元にオブジェクトを作る。これだけ書けて、十分な性能が出るなら、RDBMSでなくても、(そこそこの機能を持った) Key Value Storageで十分なんじゃないのかな、とぼんやりと思っていた。

もう一つActiveRecordで嫌いなのが、Modelのコードにカラムの情報を持たない、というとこと。DRYのためだかなんだか知らないが、これはどうみても欠点だと思っている。Modelのコードを見ただけでどういう情報が入っているかわからない。migrationのコードはツギハギだらけで後から見るには向いていない。どういうModelか、ということを知るためには、動いているデータベースにアクセスしてスキーマ情報を見るしかない。かっこ悪い。


ってなことを、漠然と思っていた頃に、タイミングよくNoSQL(KVS)ブームが到来。「ああ、これだ」と思って、ブームに乗っかって、色々なKVSを試している中で、

「これなら最近のWeb開発の多くの部分でRDBMSを捨ててもいけるんじゃないかな」

と感じさせてくれたのがMongoDBだった。

数あるKVSの中でMongoDBの何がよかったのかと言うと、

  1. RoRのModelにぴったりと合いそう。上にも書いたように、KeyとValueの関係が、そのままModelのIdとInstanceの関係になりそう。
  2. Valueの部分はスキーマフリー&ダイナミックに作成可能なので、Ruby側のコード主導でカラムの追加をすることができる。Rubyのコードでのカラムの定義が「正」になる。もちろんDRYも守られたまま。
  3. SQL-likeなこともできる。where句(のようなもの)を指定したり、maxとかminとかgroup byも使えるし、やろうと思えばストアドプロシージャ(Javascript)のようなこともできる。この辺は、SQL脳の人(チーム)にとっては移行するためのハードルが低そう。
  4. 上記を踏まえた上で、KVSらしく、RDBMSでは厄介なスケールさせることも考えられていて、Shardみたいなこともデフォルトで用意されている。

とまあ、こんなところ。

つまり、最近のリッチなフレームワークの中では、DB側にそれほどリッチな機能はいらない。RDBMSはそのいらないリッチさがあり、そのせいでスケールさせるのが難しくなったりもしている。だったら、軽量でコンパクト、でもそれなりの機能を持って、そしてスケールしやすいMongoDBで十分なんじゃないのかな、と思った。

もしMongoDB始めてみようって人がいたら、こちらをどうぞ

追記: CouchDB編もできたみたいです。私は、Railsは好きです。念のため。



resqueとRails

19th Jan, 2010 | ruby rails resque

前回 の続き(だがあんまり書くことなかった)

Railsとの話の前に、前回書き忘れてしまったのだけど、resqueには、1日1回実行する、と言ったスケジューリングの機能はない。スケジュール機能は別のそういう機能を持ったソフトウェアに任せる(代表例: cron)か、自分で作る必要がある。また、resque-scheduler といresqueのプラグインタイプの物もある。現在どの方法が良さそうかか評価中なのでそのうち書く。

さて、Railsとの連携だが、resque自体がそもそもGitHubのRailsシステム用に作られたという経緯から、もちろん非常に親和性が高い。たとえば、worker毎にRailsのEnvironmentが一回ロードされるだけなので余計な資源を食わなかったり、RailsアプリのWeb UIから非同期な処理の扱いなども簡単にできる。

さて、インストール

./script/plugin install git://github.com/defunkt/resque

gemでインストールする方法ある。詳しくは http://github.com/defunkt/resque 参照。

設定

これも特に難しいところはない。前回も書いたように必要になるのは Redis サーバを指定するところのみ。作るファイルは3つ。全部上記READMEに書いてある。

  1. config/initializers/resque.rb
  2. lib/tasks/resque.rake
  3. config/resque.yml

Workerの起動

インストールが完了するとRailsのrake taskに以下の二つが追加される。

  • rake resque:work                           # Start a Resque worker
  • rake resque:workers                        # Start multiple Resque workers

このコマンド経由で実行するとRailsの環境を読み込んだWorkerが起動し、Jobの中でRailsとまったく同じ環境でプログラミングすることができる。

optionとかは単体での起動と同じ。

例: QUEUE=default rake resque:work

非同期処理

Web系アプリではレスポンスの速さがとても大事なので、処理に時間がかかる部分は、とりあえずレスポンスだけユーザに返してしまって、残りの処理は裏でやるということがよく行われる。resqueを使うとこの処理を簡単に行うことができる。と言ってもresque側で何か特別な非同期用の仕組が用意されているわけではなく、Web側のアプリでは、単に Resque.enqueue を呼ぶだけでの話。そして、Jobの中で再度元のクラスのmethodを呼ぶということで簡単に実現できる。この辺、変に凝った仕組ではなくとても簡単でいい。

まとめ

なんか色々なことが簡単にできてしまって、何かを見落としているのではないかという気分になるresque。まだ実戦投入してないので、その後見えてくることも多いと思うので、解り次第また何か書く。

追記: 実戦投入した