データストアの新たなカタチとしてNoSQLがブームになっていますが、その中で異彩を放っているのがドキュメント指向データベースである「MongoDB」です。サイバーエージェントでは、このMongoDBを比較的早い段階から実サービスで活用しています。そこで今回はMongoDBの使いどころや利用時の注意点について、サイバーエージェントの3人の技術者にお話を伺いました。
分散処理のしくみを最初から備えるMongoDB
リレーショナルデータベース(以下RDB)ほど煩雑ではなく、分散KVS(Key-Value Store)ほどシンプル過ぎない第三のデータストアの1つとして、ドキュメント指向型データベースである「MongoDB」が挙げられます。GNU AGPLv3を採用したオープンソースソフトウェアであり、パフォーマンスが高くスケーラビリティにも優れているという特徴があります。また、JSON(JavaScript Object Notation)をベースとしたスキーマレスなデータモデルであることも魅力の1つでしょう。
このMongoDBの魅力にいち早く気付き、実際のサービスで活用しているのがサイバーエージェントです。MongoDBを使い始めた理由について、同社の名村卓氏は分散処理機構であるシャーディング(sharding)のメリットを挙げます。
「実際にMongoDBを使ったのは、アメーバピグのアメリカ版であるAmeba Picoでデータストアとして利用したのが最初になります。日本のアメーバピグはもともと自社開発の分散DBを使って運用していましたが、それをメンテナンスするのも大変だということでMySQLに切り替えました。さらにハードウェアとして高速なSSD(Solid State Drive)であるFusion-io社のioDriveを利用することで、パフォーマンス面での問題を解決しています。ただ、Ameba PicoはAmazon EC2を使ってサービスを提供しているので、特別なハードウェアを入れることができません。そこで負荷を水平分散できるデータストアを探していたところ見つけたのが、そのしくみを最初から備えているMongoDBでした」(名村氏)
もちろん、RDBの世界においても複数台のマシンに処理を分散させるしくみへの取り組みは行われていますが、そもそもRDBは原理的に分散化が容易ではありません。たとえば、オープンソースのRDBとして広く使われているMySQLにおいても分散化のためのプロダクトがいくつかありますが、「現状では、MySQLにおいて“これ”という決定打となる選択肢はない」(名村氏)のが実情です。
スキーマレスで柔軟な運用が可能
別の観点からMongoDBの導入に踏み切ったというのは、モバイルゲームの開発に携わる山田元基氏です。もともとMySQLを検討していたとのことですが、新しい機能などを加えた際にRDBに対してカラムの追加が発生したり、あるいはインデックスを張り直す必要が生じた場合にも、Amebaで月に1度か2度実施される定期メンテナンスまで待たないと作業や新機能のリリースができないという課題があったとのこと。
「そこでメンテナンスフリーで柔軟に運用できるKVSをいくつか検討しました。ただ、RDBのような処理を行うにはアプリケーション側で実装しなければならないことがネックでした。そこでRDBとKVSの中間的な存在はないかと検討したところ、MongoDBがベストではないかという話になりました」(山田氏)
用途の向き/不向き
一方でサイバーエージェントにおいて、MongoDBの導入を見合わせたケースもあったようです。Amebaのトップページである「マイページ」を改修する際に、MongoDBの導入を検討しつつ最終的には見送った経緯について、津田智光氏は次のように話します。
「マイページを改修する際、各サービスのさまざまなデータを集約して表示したいという話が出てきました。Amebaにはさまざまなサービスがあり、集約すべきデータの型も数多くあります。また、集約するデータの容量も莫大です。こうした用途にマッチしたデータストアとして、スキーマレスでありデータの分散も可能、さらに聞いた話ではパフォーマンスも悪くなさそうだったので、MongoDBを検討することになりました」(津田氏)
こうして検証した結果、最終的に採用を見送ることになった理由はパフォーマンス不足だったと言います。
「マイページはデータの更新頻度が非常に高く、MongoDBではパフォーマンスを担保しきれないと判断しました。ただ、そもそも要求する水準が極めて高かったので、しかたのない部分があるのかなとも思います。実際、MongoDBの代わりに導入したKVSのKyoto Tycoonに、SSDであるioDriveを組み合わせてやっと対応できるかどうかというレベルなので」(津田氏)
こうした経緯も踏まえ、MongoDBに向く用途として名村氏は「ソーシャルゲーム」を挙げます。その理由としては、一部のドキュメントのその一部だけを更新するといったことを簡単に実装できて、データを更新するためのロジックを作り込む必要がないことなどが挙げられました。
逆にMongoDBが向かない用途として津田氏が挙げたのは、完全なACID(※)を必要とする用途です。
「MongoDBではトランザクションの機能を持っていません。完全なACIDを保証していない(一部のAtomic処理は対応している)ため、たとえば課金系情報を保存するなどの用途には向いていません」(津田氏)
このようにお話を伺っていくと、やはりMongoDBにおいても向き不向きがあり、用途に合わせてRDB、あるいは分散KVSと使い分けることが重要であることがわかります。
高可用性を実現するレプリカセットのしくみ
データストアを選定する際、ポイントの1つとして可用性が挙げられます。データストアとして利用しているサーバにハードウェア障害が生じても、別のサーバに切り替えて処理を継続するなどといったしくみがあれば、運用面での負担を大きく軽減することが可能でしょう。
可用性を実現するためにMongoDBで提供されているのが「レプリカセット」と呼ばれているしくみです。これは1台のプライマリと複数台のセカンダリを利用し、それぞれの間で非同期でのレプリケーションを行い、プライマリが「落ちた」場合にはフェイルオーバーを行うというもの。これによって高い可用性を実現することが可能になります。
ただ、レプリカセットの目的はあくまでも高可用性を実現することです。レプリケーションによって複数台のサーバを同期して負荷分散を行い、パフォーマンスを向上させるためのしくみではありません。セカンダリからデータを読み出すしくみは用意されていますが、基本的にはフロントに立っているのはプライマリのみで、3台のサーバを用意しても性能は1台分ということになります。そのため、スケールアウトさせたい場合はシャーディングを組み合わせて使うというのがMongoDBの基本的なスタンスです。
こうしたしくみのため、ハードウェアの利用効率はけっして高くないと名村氏は指摘します。
「たとえば5台分のパフォーマンスが必要な場合、レプリカセットを5セット追加するという話になります。そのレプリカセットをプライマリ1台、セカンダリ2台で構成している場合、5×3=15台のマシンが必要となり、物理サーバで構成している場合は相当数のサーバが必要になります。ちなみに現状ピグライフでは50台くらいを利用していますが、それでも足りないのであと30台くらい追加しようとしています」(名村氏)
このようにハードウェアの利用効率はよくありませんが、「足せばとりあえず(シャーディングの機能によって)パフォーマンスが確保できるので、お金さえかければ何とかなるという意味では楽といえば楽ですね」(名村氏)とのこと。
さらに「MongoDBはスキーマレスであるため、データのヘッダ情報を全ドキュメントで持っているので、ディスク領域という意味でもあまり効率は良くありません」と指摘するのは津田氏です。圧縮するしくみがなく、個々のデータはバイナリでもカラム名などはテキストで保持していることも響いているようです。特にシャーディングのノード数を絞りつつ、大規模なデータを保存したいといったケースでは注意が必要でしょう。
MongoDBを利用したアプリケーション開発での注意点
さて、MongoDBは比較的新しいデータストアであり、開発時において注意することも少なくありません。たとえばNode.jsを使ってピグライフを開発した名村氏は、Node.jsからMongoDBに接続するドライバでトラブルがあったと話します。
「実はMongoDBのNode.js用のドライバは公式としては存在していません。ただ、オープンソースのドライバがいくつかあり、そのうちの1つをピグライフでは使っているのですが、当初はドキュメントが壊れたり、あるいはプロパティの数によって2つのドキュメントが保存されてしまったりと、けっこうひどいバグがたくさんありました。最近はだいぶ改善されていますが、まだこちらで作ったパッチを適用しないとあるタイミングでフリーズしてしまうなど、現状では安心して使える状況とは言い難いですね」(名村氏)
さらにもう1つ、JavaScriptでMongoDBを利用する際に注意すべき点として名村氏が挙げるのはJSONとBSON(Binary JSON)の相性です。MongoDBではドキュメントをBSONと呼ばれる形式で保存しますが、このBSONとJSONを相互変換する際、格納されているデータの型によって問題が生じる場合があるとのこと。具体的にはJavaScriptではサポートしていない64ビットの整数をBSONからJSONに変換しようとしたとき、文字列になったりdouble型になったりといったことが起きると指摘します。ただ、こうした相互変換の問題はドライバが原因ではないかとのことで、今後ドライバの改善が進めば解消される可能性はありますが、しばらくの間は注意が必要のようです。
ドキュメントのサイズにも注意が必要と説明するのは津田氏です。次々とデータをドキュメントに格納していると、ドキュメントが肥大化してしまい、ドキュメントサイズの制限を超えてしまってエラーが発生したり、あるいはパフォーマンスが低下したりすると言います。
「最初、あまり深く考えずにとりあえず動くものを作ろうと開発していたときだったのですが、ドキュメントサイズの制限を軽くオーバーしてエラーが発生したことがありました。MongoDBのバージョンアップによってドキュメントサイズの制限は多少緩和されていますが、ドキュメントサイズが大きくなると更新が遅くなるなどの弊害があり、またネットワークにも負担がかかるため、ドキュメントが肥大化しないように設計の段階から注意すべきだと思います」(津田氏)
パフォーマンス低下を防ぐコツ
最後にパフォーマンスを向上させるためのコツについて伺ってみました。特にパラメータ調整を施さずともそこそこのパフォーマンスを発揮するMongoDBですが、性能の低下を抑えるためにはどういった点に注意すべきなのでしょうか。
開発時における注意点として、名村氏は「データを更新する際、ドキュメントを丸ごと取ってきて中身を書き換え、ドキュメントごと保存するような処理はやめたほうがよい」と話します。その理由となっているのが「oplog」の存在です。
oplogとはデータ更新に対するオペレーションを記録した一種のデータベースであり、MongoDBのレプリカセットでは、このoplogを利用してレプリケーションを行っています。当然ながらデータ更新を行うと必ずoplogのための処理も発生するため、大きなデータの更新を行えばそれだけ負荷も高まってしまうというわけです。
さらにシャーディング時にどのキーを使って分散するかの調整も重要とのこと。できる限り分散して処理ができるようにキーを設定するのが基本ですが、どういった処理が多いのかによって考え方は変わると津田氏は説明します。
「たとえば前方一致で最初の10件だけを取得するといったクエリを投げる場合、その10件が分散しているとすべてのサーバに対してクエリを投げるという処理が発生しますが、1つのサーバに集約するようにキーを設定しておけば高速に結果を得られます。一方、何らかの値を連続して書き込むというケースでは、複数のサーバに分散したほうが短時間で処理が終了します。このため、使い方に合わせてキーを設定することが重要です」(津田氏)
早い時期からMongoDBを導入してきたサイバーエージェントならではの導入と開発のためのノウハウを紹介しましたが、参考になったでしょうか。次回は運用面を中心に、さらにMongoDBの活用術に迫ります。
- サイバーエージェント公式エンジニアブログ
- URL:http://ameblo.jp/principia-ca
エンジニアの生の声を週替わりでお届け中!