MongoDBを半年運用してみた(のフォロー)

某社との合同勉強会のLTで発表したMongoDBを半年運用してみたが、えらいはてブされててびっくり。。。あのままだとMongoDBは絶対使わないって結論になっちゃいそうなので、自分をフォローする形でエントリを書きたいと思います。

資料はこちら。


コネクションエラー多発

これは、7月にmongosの場所がmongodからSocketサーバ/Webサーバ/管理サーバに移動した件と関係しています。


まず、Socketサーバ/Webサーバ/管理サーバともにJavaで実装されているので、MongoDBへの接続はJavaの公式ドライバーを利用しています。当初のドライバーの利用方法は、

// Shard1, Shard2, Shard3のPRIMARYと同居してるmongosのホストアドレス
for (String hostAddress : hostAddresses) {
    String[] split = hostAddress.split(":");
    String host = split[0];
    int port = Integer.parseInt(split[1]);
    ServerAddress address = new ServerAddress(host, port);
    addresses.add(address);
}
Mongo mongo = new Mongo(addresses, options);

といった感じでした。が、よくよくJavadocを見てみると、コンストラクタに以下の記述が。

Creates a Mongo based on a replica set, or pair.

ここで渡せるホストアドレスは、ReplicaSetまたはReplicaPair(2台1セットの構成。現在非推奨)の全ホストのようです。設定した中からPRIMARYを探し出して接続し、PRIMARYが死んだ場合は次に昇格したPRIMARYに接続し直す、と。つまり、Sharding構成の場合にそれぞれのmongosのホストアドレスを指定してたのは誤っていたようです。
7月以前は、Socketサーバ/Webサーバ/管理サーバの合計7台でShard1〜Shard3のPRIMARYと同居してるmongosを指定してました。結果的に、全てのクライアントが最初に記述されてるmonogos(Shard1のPRIMARYと同居してる)につなぎにいって、コネクションが枯渇していたと思われます。
そもそもmongosは、クライアントと同居させた方が良いようです

mongos processes can run on any server desired. They may be run on the shard servers themselves, but are lightweight enough to exist on each application server.

という経緯があって、7月にはmongosを各クライアントと同じサーバで動作させることにしました。結果、今のところコネクション枯渇エラーは発生していません。

のようにmongosが死ぬことはありえるので、現状ではプロセス監視と自動起動で逃げています。この対応も不十分なので何とかしたいところ。
JavaのDriverに関する詳細はJava Driverのコネクションプールについて調べてみたもご参照下さい。

自動マイグレーションが止まらない


これはメリットでもありデメリットでもあります。
データ量が多くなければ自動マイグレーションは本当に便利で、すごく手軽にShardを追加できます。Picoの場合はデータ量とコレクションのオブジェクト数がそれなりに多い(7月時点でオブジェクト数は3.5億、データ量は150GB程度)ので、自動マイグレーションに頼ると新規でShardを追加したときは厳しかったです。

現時点では、自動マイグレーションアルゴリズムはアクセス頻度ではなくあくまでデータ量でのバランシングなので、より効率的なデータ配置を行いたい場合は、自動マイグレーションをOFFにすることも必要かと思います(自動マイグレーションをONの状態で手動で移動することも出来ます。その場合は自動マイグレーションで、データが再び元のShardに戻されるケースもありえます)。
詳細は MongoDB Auto-Sharding at Mongo Seattleオライリーから出版されているScaling MongoDBのBlancingのページをご参照下さい。

異なるバージョンのMongoDBが混在

これは完全に自分のオペミスでした・・・。今振り返ると、そのまま新しいバージョンにするという選択肢もあったんですが、調査が不足していて元のバージョンに戻すという対応を取りました。
1.6系から1.8系へのバージョンアップの方法についてはUpgrading to 1.8をご参照下さい。注意点としては、

1.8.x secondaries can replicate from 1.6.x primaries.
1.6.x secondaries cannot replicate from 1.8.x primaries.

ですね。PRIMARYを1.8系に変えた時点で、1.6系に戻すのは厳しいと思います。

バージョンアップでchunkサイズが変わった

検証したときは問題なかったんですが、本番環境で発生してしまいました。。。発生する条件がはっきりしないんですが、id:tm8r さんのchunkSizeではまる のエントリが関連してそうな気がします。おとなしく--chunksizeを起動時に渡すのが正解だったかも。

データの同期に失敗


サーバの入れ替え作業として、ReplicaSetの状態を

Member1 PRIMARY
Member2 SECONDARY
Member3 SECONDARY
Member1 PRIMARY
Member2 SECONDARY
Member3 SECONDARY
Member4 SECONDARY
Member5 SECONDARY
Member6 SECONDARY
Member4 PRIMARY
Member5 SECONDARY
Member6 SECONDARY

と変更していく手順で行おうとしていました。が、2番目の状態でメンバーの数が偶数となってしまい、かつMember4〜Member6が同期が取れない状態となり、PRIMARYが起動出来なくなるという自体になってしまいました。

Member1 SECONDARY
Member2 SECONDARY
Member3 SECONDARY
Member4 still initializing
Member5 still initializing
Member6 still initializing

詳細はMongoDBでReplicaSetにメンバーを追加/削除する方法と、その注意点 に書いてあります。こちらは完全に検証不足でした。。。いま考えると、Member4〜Member6のlocalのdbsを消して再起動をかければ良かったかも。

同じShardのmongodの2台がEC2インスタンスごと死亡

これはもうどうにもこうにも。EC2の問題なので・・・。3台1セットのReplicaSetであれば、2台同時に死ぬことは普通ない・・・はず。と思うしかないです。。。

mongodが落ちる

これは滅多にないんですが、とはいえたまにあります。1台だけならまだいい・・・と思いつつも、PRIMARYが落ちた場合はSECONDARYの同期が取れてないデータが存在してしまう可能性もあるので、怖さはあります。バージョン上がればもっと安定するかな・・・。

まとめ

バージョンの移行とかサーバの入れ替えとか、通常行わない作業でハマったケースが多かった感じです。今から最新のバージョンでやる分には、あまり問題にならないかと思います。mongod/mongosがたまに落ちる件はまだ怖いんですが。。。

MongoDB自体は、ReplicaSetやAuto-Shardingの仕組みを最初から備えていて、とっても素晴らしいプロダクトだと思います。インフラ担当がほとんどいない状態でも運用出来てるのは、これが大きいです。ドキュメント指向なので複雑なデータも扱えますし、ドキュメント内にインデックスを貼ったり、ドキュメントの一部を取得したり更新することも出来ます。
通常のRDBでは扱いづらいもの、個人的にはソーシャルゲーム系では結構向いてるんじゃないかなと思ってます。階層を持つようなデータはどうしてもRDBだと扱いづらいですし。ユーザが爆発的に増えた場合には自動マイグレーションが威力を発揮するかと。

そんなわけで、I love mongo、使ってることは全然後悔してないです!もっともっと安定させてあげたい!

余談

ちなみにこれ、LTで発表したネタ資料ですので・・・。いろいろ説明足らずなところがあって申し訳ありませんでしたm(_ _)m

余談その2

Picoのサービスは2010年3月からなので、もう1年半近く運用してます。当初は独自KVSで、MongoDBに移行したのは2010年9月だったようです。自分がJoinしたのが2011年1月からだったので、半年の運用と表現させて頂きました。