ソーシャルゲームにおけるmongodb適用事例 - animal land
DESCRIPTION
Mongo Tokyo 2012で発表した資料。Animal LandでMongoDBを利用する際に考慮した点などなど。TRANSCRIPT
ソーシャルゲームにおけるMongoDB適用事例
Masakazu MatsushitaCyberagent, Inc.
•松下 雅和 / Masakazu Matsushita•@matsukaz• Cyberagent, Inc. - SocialApps Division- Ameba Pico (2011/01~)•海外向けピグ- Animal Land (2011/03~)• DevLOVE Staff
About Me
•サービス概要•システム構成•MongoDB採用のポイント•MongoDB利用時に考慮した点•発生した障害•今後の課題• etc
Agenda
サービス概要
Demo
• 2011/03から開発スタート•初ミーティングが 3.11•本格的な開発は2011/05から• 2011/12/09 リリース
開発期間
• Producer × 2• Designer × 1• Flash Developer × 3• Engineer × 4 + α
開発メンバー
システム構成
• Amazon Web Services- EC2 (Instance Store + EBS)- S3- CloudFront- Route53- Elastic Load Balancing• Google Apps• Kontagent
利用サービス
システム間のつながり
HTML
Flash
iframe
Commandサーバ
Webサーバ
JSON
AMF
各種API呼び出し課金コールバック
AMF : Actionscript Message Format
サーバ構成
Shard
Web
ELB
nginxTomcatmongos
CommandnginxTomcatmongos
×3 ×4
MongoDB
ELBS3
CloudFront
Route 53
admin
Tomcatmongos
monitor
ビルド
jenkinsMaven
バッチバッチMySQL
L
×5
mongod
MongoDBmongod
MongoDBmongod
MongoDBmongoc
×3
MySQLMySQL
MySQLMySQL
memcachedmemcached
×2XX
XX
XX
L
L
L
L
L
L
L
L
muninnagios
nginx
L
XX
m1.largem2.2xlarge
XX
redmine
SVNEBS EBS
EBS
Secondary EBS
EBS EBS
EBS
• nginx 1.0.x• Tomcat 7.0.x•MongoDB 2.0.1•MySQL 5.5.x• memcached 1.4.x
ミドルウェア
• Spring Framework、Spring MVC• BlazeDS、Spring Flex• Spring Data - MongoDB 1.0.0 M4•mongo-java-driver 2.6.5• Ehcache• spymemcached• RestFB•MyBatis
フレームワーク/ライブラリ
MongoDB採用のポイント
• Ameba Picoでの利用実績
http://apps.facebook.com/amebapico/
•MongoDBの良さ- ReplicaSetによる耐障害性- Auto-Shardingによるスケールしやすさ- 複雑なデータ構造を扱える- Index、Advanced Queries- 開発チームがアクティブ- 敷居が低い
• Animal Landの要件に合う- 複雑なデータ構造 (街のグリッド情報、ユーザ/建築物のパラメータ、etc)- シーケンシャル処理中心で、同時更新が少ない- メンテナンスレス•データ構造の動的変更•耐障害性•スケールアウト
•苦手な部分は他の方法で解決- 特性に合わないデータは他のDBで •信頼性が必要な課金関連はMySQL•一時的なデータはmemcached- トランザクションは利用しない
MongoDB利用時に考慮した点
•トランザクションがいらないデータ構造に- ユーザ情報などは、1ドキュメントで保持して一括更新
{ “facebookId” : xxx, “status” : { “lv” : 10, “coin” : 9999, ... }, “layerInfo” : “1|1|0|1|2|1|1|3|1|1|4|0...”, “structure” : { “1|1” : { “id” : xxxx, “depth” : 3, “width” : 3, “aspect” : 1, ... }, ... }, “inventory” : [ { “id” : xxx, “size” : xxx, ... }, ... ], “neighbor” : [ { “id” : xxx, “newFlg” : false, ... }, ... ], “animal” : [ { “id” : xxx, “color” : 0, “pos” : “20|20”, ... } ], ...}
ユーザ情報イメージ
アプリケーション開発時
•データ量を可能な限り削減- 街のグリッド情報を1つのデータで保持するなど (プログラム側で展開)- 設計時の構造(500 KB)“layerInfo” : { “1|1” : 0, “1|2” : 1, ....}
“layerInfo” : “1|1|0|1|2|1|1|3|1|1|4|0...”
- 現在の構造(50 KB)
アプリケーション開発時
•フィールド数は多すぎないように- 2万以上のフィールド (144x144の街のグリッド情報 )を持つと、find()にっかる時間は5秒以上に・・・- データ量だけでなく、BSONのパースにかかる時間も考慮する
アプリケーション開発時
• Shard Keyは以下の方針で決定- カーディナリティが低い値は使わない- よく使われるデータがメモリ上に乗る- 使われないデータはメモリ上に乗らない- 参照や更新が多いデータはバランスよく各Shardに分散- Targetedオペレーション での利用を意識 (後述)
おすすめ書籍 →
アプリケーション開発時
• Targetedオペレーションを中心に- Shard Keyでデータを操作- Shard Key以外の操作はIndexを利用
Operation Typedb.foo.find({ShardKey : 1}) Targeteddb.foo.find({ShardKey : 1, NonShardKey : 1}) Targeteddb.foo.find({NonShardKey : 1}) Globaldb.foo.find() Globaldb.foo.insert(<object>) Targeteddb.foo.update({ShardKey : 1}, <object>)db.foo.remove({ShardKey : 1})
Targeted
db.foo.update({NonShardKey : 1}, <object>)db.foo.remove({NonShardKey : 1})
Global
アプリケーション開発時
•更新頻度をなるべく減らす- マスタ情報はサーバ上でキャッシュ- ユーザ操作はまとめて処理 (5秒に1度)• Flash側でキューの仕組みを用意•サーバ側はキューから取り出してシーケンシャルに処理
Flash Commandサーバ
まとめて送信
キューキューで保持
シーケンシャル処理ユーザ操作
アプリケーション開発時
• O/Rマッパーで開発を効率化- Spring Data - MongoDBとラッパークラスを用意して効率よく開発@Autowiredprotected MongoTemplate mongoTemplate;
public void insert(T entity) { mongoTemplate.save(entity);}
- オブジェクトをそのまま利用できるので、RDBのO/Rマッパーより扱いやすい- Javaでも言うほど大変じゃない
アプリケーション開発時
•ロールバック出来ない前提で実装- ある程度のデータ不整合は諦める- 必ずユーザが不利益を被らないようにする
アプリケーション開発時
•従来のDBと同様に見積もり- データ量/1ユーザ (50 KB)- 想定ユーザ数 (DAU 70万)- データの更新頻度- 各サーバの最大同時接続数- 各サーバの台数、スペック、コスト- 想定ユーザ数を超えた場合のスケールのポイント整理
インフラ構築時
•性能検証- 帯域幅を確認 (350Mbps程度)- MongoDBを検証 (MongoDB 2.0.1、ReplicaSetのShardを2つ、ジャーナリング有効)•参照/更新の処理性能•マイグレーション時の性能比較• Javaドライバー経由の性能比較- アプリケーションを通した性能を確認
インフラ構築時
•障害を想定した動作検証- mongodが落ちた場合•PRIMARYへ昇格するか•起動したらデータが同期されるか•SECONDARYが落ちても問題ないか- mongocが落ちた場合•全体の動作に影響がないか- バックアップから戻せるか
→ 全て問題ナシ
インフラ構築時
• ReplicaSet、Shardの構成と配置を決定- メモリサイズを大きめ- ディスクI/Oが高速- mongocはmongodと別サーバに- 信頼性を高めるためにEBS利用 (負荷軽減のためSECONDARY専用に)
•ジャーナリングを有効に• oplogsizeを10GBに (デフォルトの全体の5%が多すぎるため)
インフラ構築時
•必要なIndexはあらかじめ作成しておく- 通常のIndex作成中は、全てのオペレーションがブロックされてしまうため- 20万件のデータ (それぞれ50KB程度) にIndexを貼ると、2分程度かかる- あとから追加する場合は、メンテ中に作成するか、バックグラウンドで作成
インフラ構築時
•コネクションプールをチューニング
項目 意味 値connectionsPerHost コネクション数 100threadsAllowedToBlockForConnectionMultiplier
1コネクション辺りの接続待ち数 4
※ mongosに対するコネクションプール
- nginxのworker数、TomcatのThread数とのバランスを考慮して設定- プールが足りなくなったときのエラー (Out of Semaphores) に注意
上記例では、100 + (100 * 4) = 500
インフラ構築時
• db.printShardingStatus()でShard間のchunkの偏りを定期的にチェック- 必要があれば手動でchunk移動
•新しいCollectionの追加は慎重に- 最初はPrimary Shardにデータが偏り、急激な負荷がかかる可能性があるため
運用時
発生した障害
•mongoc1台 & mongod1台 (SECONDARY) が落ちた•原因はEC2インスタンスの仮想サーバホストの障害、MongoDBは悪くない
•サービスへの影響はゼロ!•プロセスを起動するだけで復旧!!
mongodとmongocダウン
今後の課題
•やはり難しい•当面はメンテを入れてバックアップ•以下のSECONDARYバックアップも検討中1. balancerをオフに2. SECONDARYでwrite lockをかけてディレクトリ毎バックアップ
3. SECONDARYのロックを解除
オンライン・バックアップ
•サービスを運用しているとUpgradeのタイミングが難しい•以下のアップグレードの手順が必要1. Arbiterを停止、Upgrade、起動2. SECONDARYを停止、Upgrade、起動3. PRIMARYを停止、Upgrade、起動
Upgrade
• Shard追加は可能だが、追加時のバランシングがパフォーマンスに与える影響が読めない- Picoではパフォーマンスの影響が問題となったため、メンテ中に対応している
Shardの追加
•ユーザ情報のデータサイズが大きかったため、デフォルトの64MBだとマイグレーションが多発•データサイズに合わせて調整する必要がある
Collectionごとにchunksizeが設定出来るようになって欲しいです・・・
最適なchunksize
•ユーザ情報を1つにまとめたため、データの分析がやりづらい•必要に応じてIndexを貼って対応中- MapReduceも試したが、検証時にパフォーマンスに影響が出たので利用していない
データの分析
ご清聴ありがとうございました