- 8ヶ月間使ってデータベースの規模は、
- Collections (tables): 17,810
- Indexes: 43,175
- Documents (rows): 664,158,090
- master/slaveのマニュアルでのフェイルオーバ環境で運用してきた。
- masterは72GBのRAM
- slaveは別のデータセンタ
- ディスク的にきつくなってきたので、手動でShardingをし4つのDB(Master 2つ / Slave 2つ)に分けることにした。
- namespaceの限界があるので、データを3つのMongoDB( これは物理的なサーバではなくてMongoDBのデータベースの単位)に分割している。
- 現在のnamespaceの数は、 db.system.namespaces.count() で確認することができる。
- MongoDBはデータベース単体でのDurabilityを持っていない(なぜ持たないかという公式の見解)。
- そのため、必ずレプリケーションをし複数のデータベースで運用する必要がある。
- もし、停電とかが起きDBがおかしくなった場合は、repairする必要がある。
- これは私たちぐらいのサイズのデータを持っている場合、数時間かかることになる。
- master/slave構成(そして理想的にはslaveは別のデータセンタ)を取ることができる。ただし、masterの問題があったら手動でfailoverする必要がある。
- または、replica pairs(訳注: 複数のmasterを持ち動的に切替える機能)を使うと、その辺は自動的にできる。
- とても巨大なデータベースなので、新しいslave(別のデータセンタでVPN接続)へのコピーは48-72時間かかる。
- この間、slaveが最新のmasterと同期しなくなる、というリスクがある。
- また、sync中に更新されれたデータを保持しておくためのop logのサイズには注意が必要。
- 75GBのop logが必要だった。
- これは–oplogSizeで指定できる
- しかし、MongoDBが起動しコネクションを受け付ける前にファイルがallocateされ、この環境では5分くらいかかり、その間MongoDBは使えなくなる。
- これを防ぐためには手動でファイルを前もって作成しておけばいい (訳注: やり方は原文参照)
- しかし、MongoDBが起動しコネクションを受け付ける前にファイルがallocateされ、この環境では5分くらいかかり、その間MongoDBは使えなくなる。
- これは–oplogSizeで指定できる
- 75GBのop logが必要だった。
- 最初のsync中に”遅くなる”問題
- 空のslaveにコピーする最中、アプリケーションの速度が遅くなった。
- それでも十分な速度は出ていたので詳細の調査はしなかったが、データベースの接続とクエリーが少し遅くなっていたみたい。環境によっては問題になるかも。
- 空のslaveにコピーする最中、アプリケーションの速度が遅くなった。
- daemon/forkプロセスとlogging
- –fork パラメータをつけてforkされたプロセスで動かしている。
- –logpath を指定するとerrorを吐き出せる
- --quiet オプションもつけてログが大きくなりすぎ内容にしている
- 組み込みのlogローテーション機能はない
- –fork パラメータをつけてforkされたプロセスで動かしている。
- OSの調整
- 最大のファイルオープン数に到達してしまった。
- (ここの環境では)デフォルトで1024に設定されていて、これは小さすぎるかも。参照。
- atimeをdisableにした。
- 最大のファイルオープン数に到達してしまった。
- インデックス作成でのブロック
- 全部のインデックスを最初に作成してあったので即座に作成できたが、もし既存のコレクションに対してインデックスを作成する場合、作成中はデータベースがロックされる。
- MongoDB (1.3)では background indexing があるので大丈夫。
- 全部のインデックスを最初に作成してあったので即座に作成できたが、もし既存のコレクションに対してインデックスを作成する場合、作成中はデータベースがロックされる。
- 効率的なディスクの再利用
- 私たちが運用しているサーバーをモニターするというアプリケーションの特徴から、たくさんのデータを集める。
- しかし、設定された期間を過ぎるとデータは削除される。
- こういうことをしているとディスクを食うようだ。
- 新しいSlaveへデータをコピーして気づいた。(コピーした後でmaster 900GB / slave 350GB だった)
- こういうことをしているとディスクを食うようだ。
- しかし、設定された期間を過ぎるとデータは削除される。
- MongoDBの有料サポートへ連絡済。
- 私たちが運用しているサーバーをモニターするというアプリケーションの特徴から、たくさんのデータを集める。
- 素晴らしいサポート
- gold commercial support service (有償サポートサービス)を使っている。上記のような色々な問題が起きたときとても便利だった。チケットを開くのは簡単だし、サポートもとても速い。また24時間対応の緊急の電話もできる。
- お金を払う必要がない/払いたくない場合でもメーリングリストはとっても便利。
- gold commercial support service (有償サポートサービス)を使っている。上記のような色々な問題が起きたときとても便利だった。チケットを開くのは簡単だし、サポートもとても速い。また24時間対応の緊急の電話もできる。
- まとめ - MongoDBを選択したのは正しかったか?
- Yes
- データベースの管理も簡単だし、スケールもよくする。
- 今一番欲しい機能はauto sharding (訳注: 現在アルファバージョン)。
- 今はmanualでshardingしているけど、それがあればもっといい!
- 今一番欲しい機能はauto sharding (訳注: 現在アルファバージョン)。
- データベースの管理も簡単だし、スケールもよくする。
- Yes
こういう人柱的な人(会社)にはほんと感謝です。
そもそものきっかけは、ここ半年間くらい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成分が増していく。全然楽しくない。
つまり、
- SQLっぽく書かずに、Ruby(ActiveRecord)っぽくきれいに書くように頑張る
- でも、ロジック的/パフォーマンス的に頑張るのが限界だった場合に、SQLっぽいコードをRubyに書くことになる
- そうこうしているうちに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の何がよかったのかと言うと、
- RoRのModelにぴったりと合いそう。上にも書いたように、KeyとValueの関係が、そのままModelのIdとInstanceの関係になりそう。
- Valueの部分はスキーマフリー&ダイナミックに作成可能なので、Ruby側のコード主導でカラムの追加をすることができる。Rubyのコードでのカラムの定義が「正」になる。もちろんDRYも守られたまま。
- SQL-likeなこともできる。where句(のようなもの)を指定したり、maxとかminとかgroup byも使えるし、やろうと思えばストアドプロシージャ(Javascript)のようなこともできる。この辺は、SQL脳の人(チーム)にとっては移行するためのハードルが低そう。
- 上記を踏まえた上で、KVSらしく、RDBMSでは厄介なスケールさせることも考えられていて、Shardみたいなこともデフォルトで用意されている。
とまあ、こんなところ。
つまり、最近のリッチなフレームワークの中では、DB側にそれほどリッチな機能はいらない。RDBMSはそのいらないリッチさがあり、そのせいでスケールさせるのが難しくなったりもしている。だったら、軽量でコンパクト、でもそれなりの機能を持って、そしてスケールしやすいMongoDBで十分なんじゃないのかな、と思った。
もしMongoDB始めてみようって人がいたら、こちらをどうぞ。
追記: CouchDB編もできたみたいです。私は、Railsは好きです。念のため。
追記: 最新情報はこちらです。
MongoDBが流行ってきてる風なので、これだけ読んでおけばMongoDBの雰囲気がわかるだろうってところを、日本語訳が終わっているところから集めてみた。
なるべく順に読めるように並べたつもりだけど、前後してるところもあると思うので、とりあえず読み進めるのをお勧め。
まず、何はなくとも、
次にコレクションを触るためのシェル。MongoDBのシェルというのは、RDBMSでいうとSQLを直接叩くところで、PostgreSQLのpsqlコマンド, MySQLのmysqlコマンドみたいなもの。
実際にMongoDBを使って開発する場合、直接MongoDBを操作するよりも、各言語(PHPとかRubyとかJavaとか)のマッパー経由で使うことが多いとは思う。しかし、SQLを知らないとO/Rマッパーを使いこなすのが難しいように、シェルからのデータ操作は最初に覚えて置いた方がいいだろう。シェルでの操作は、SQLと見た目はずいぶん違うけど、SQLの考え方と似ていることも多いので、RDBMS経験者ならすぐに理解できるはず。
チュートリアルをやって感じを掴もう。とりあえず読むだけでもok。
KVSなデータベースなMongoDBだけど、SQLっぽい色々なクエリーをサポートしている。
性能面に関しての設計とかノウハウ。
Shardingが簡単にできるところがMongoDBの魅力の一つ。
その他最初のうちに読んで面白そうなところ。
誤訳とか指摘してくれると嬉しい。日本語typoの指摘も大歓迎。ここのコメントでもいいし、Twitterでもok。
ドキュメントはまだまだ残っているので、まったりと更新していく予定。
enjoy!