SQLer 生島勘富 のブログ

RDB・SQLの話題を中心に情報発信をしています。

「艦これ」から、ソーシャル系のサーバ構成を考える

 私は、ソーシャル系とは縁遠い仕事ばっかりしているのですが、そういう依頼も若干増えてきたので話題になっている「艦これ」をお盆にやってみた。

 残念ながら、「艦これ」の魅力は分からなかった。しかし、ミッションを用意されると、「クリアーしたい」という欲求から意地になるのは、何となく理解できました。それより、同時に始めた「Clash of Clans」には嵌まりました。気になっていた「ゲームの中に如何に自然に課金システムを取り入れるか」という課題についても、個人的には「Clash of Clans」の方が上手に解決しているように思います。

 「艦これ」は、同時アクセスが10万以上あって、何度かシステム障害があったとのこと(そりゃあるでしょうが……)。私の興味の方向性は、課金システムであったり、システム構成にあるので、「艦これ」のシステム障害の方が強い興味の対象になります(苦笑)

 というわけで、「ソーシャル系で起こりがちな問題」というのを考えてみたい。
 「艦これ」は既に辞めて、参加待ちの人に席を譲りましたので、記憶の範囲になってますし、あくまで想像ですのでご了承ください。

SQLを使うか、NoSQLを使うか

 SIer系の技術者でも SQL が苦手な人が多いのですが、ソーシャル系の技術者ではもっと SQL が苦手な人が多いようです。そんな状況で、なぜ RDBMS を選択するのか私にはイマイチ理解できないのですが、「艦これ」に必要なデータは(画像、音声ファイルを除けば)非定型なものがなく、マスターデータは、攻略Wikiにほぼ再現されている程度です。整理すればエクセルでも事足りるレベルですので、確かに RDBMS を使った方が楽に開発できると思います。

 噂によると、「艦これ」では、MySQL Cluster を使っているとのことです。MySQL Cluster は良くできていて、SQL と NoSQL(SQLをショートカットしたAPI)のいずれでも利用可能ですから、「艦これ」のシステムを考えれば、参照系はSQL、更新系はNoSQL(API)で使うのが妥当でしょう。

 もし、更新系でNoSQL(API)を使用していなければ、かなり「痛い設計」ですが、有料オプション(のはず)
※ CGE(Carrier Grade Edition)に含まれていて、CGEは一部有料ですが、NoSQL(API)は無料のようです。
なので、大人の事情で NoSQL(API)を使ってない可能性もありますね。しかし、変更点は10人日程度あればできるでしょうから、当初、SQLを使っていたとしても、もう直している頃でしょう(ですよね!)

JOINを避ける?

 MySQL Cluster を使うなら、これまで書いてきたように効率的に SQL を書かないと意味がない。JOIN を避けたりしたら返ってDBサーバの負荷が掛かります。

 しかし、MySQL Cluster を使わないと考えて、「(お手製の)スケールアウトに備えて JOIN を禁止する」という話をよく聞きます。(例えばこんな記事 http://d.hatena.ne.jp/iad_otomamay/20110808/1312805917)

 「艦これ」は直接関係ないのですが、「JOINの禁止」が本当に正しいか考えてみます。

 結論から言うと、JOIN を避けスケールアウトしたところで DBサーバのパンクの可能性は減らないどころか、逆に増えることになります。

 RDBMS は、大量のデータを処理することを目的に作られて、現在のハードウェア環境であれば CPU を使いきることはかなり難しく、メモリーが足りなくなることがほとんどです。特に、一つ一つの処理は単純で、大量のアクセスを捌くことが重要なソーシャル系のシステムでは、「メモリーを効率的に利用することだけを気にしていれば問題ない」と言い切っても過言ではありません。

 そのメモリーの使い方ですが、データを毎回ハードディスクから取り出していては間に合わないので、メモリーの7〜8割をデータのキャッシュに割り当て、残りを作業エリアに割り当てるのが一般的でしょう。

 残りの作業エリアがどのように利用されるかは、コネクションとセッションについて理解する必要があります。APサーバとDBサーバは、まず、コネクションを確立します。コネクションを確立する処理は非常に重く、処理の度に行うわけにはいかないので、通常、接続したままにして使い回します(コネクションプール)

 図の様になり、DBサーバはコネクションの中にセッションを作り、セッションの中でクエリーを発行します。

 クエリーを処理するためにセッション毎に利用するワークメモリーの大きさは、初期パラメータの Sort_Buffer_Size で指定します。(動的にも変更可能)
 通常、Sort_Buffer_Size は 128KByte〜2MByteぐらいで指定します。

 一連の処理で、別々にコミットしたい処理などのときにセッションを分けることがありますが、ソーシャル系のシステムなら、ほぼ、1コネクションに対して、1セッションで十分でしょう。しかし、同時アクセスが10万でその通りDBサーバで処理するなら、小さめの 128KByte の Sort_Buffer_Size を指定しても、

 128KByte × 100,000コネクション(セッション) = 12.8GByte の作業メモリーが必要になります。

 MySQL の設計上の最大コネクション数は10万ですが、1つのコネクションにつき OS のスレッドを1つ必要とし、OS もスレッド毎にメモリーを確保しますので、現実的には DBサーバは10万もの同時アクセスはなかなかこなせません。ですから、APサーバがコネクションプールを作って、DBサーバに対する最大のアクセス数をコントロールするわけです。そのため、大量アクセス時のボトルネックは コネクション数か、それに対するメモリー量 となることがほとんどです。

 ※ DWH(Data Ware House)などのシステムは、同時接続ユーザ数が数名〜数十名となるので、コネクション数がボトルネックになることはまずなく、1つの処理でスワップするような大きな処理の場合は、分割が必要になります。

JOINを避けてDBサーバをスケールアウト

 「艦これ」のようなシステムの場合、一人のユーザ情報が2KByteで100万ユーザいたとしても 2GByteですから、マスター系は一切スケールアウトは必要ありません。そのようなシステムでJOINを避けてスケールアウトしたとすると、図の様になるでしょう。
 

 JOINをするような処理の場合、1処理が終わるまでの間、どちらのコネクションも解放できません(しません)から、スケールアウトしてサーバ数を増やしても、スケールアウトしなかったDBサーバのコネクション数を増やすことができず、スケールアウトする前と同じ状態のボトルネックになってしまう(Sort_Buffer_Size を小さくできれば話は別ですが……)3台にスケールアウトすれば、スケールアウトしないDBサーバへのコネクションプール数は、スケールアウトしたDBサーバへのコネクションプール数の3分の1にしなくてはならないのですが、全部、スケールアウト前のコネクションプール数にしたままにして、スケールアウトした方が不安定になるなんてことも、見たことがあるような、ないような……(苦笑)

 JOINに限らず、APサーバで処理をすることによって遅くなるなら、DBサーバに対するコネクションを掴んだままになるため、結果的にDBサーバに負荷を掛けてしまうのです。

 つまり、無駄に遅いだけで、ユーザにも、システムにも何も良いことはありません。

「複雑だから負荷が高い」と思うのは大きなお世話!

 複雑だろうが、単純だろうが、シビアな要件になればなるほど、「速いことが正義」になります。

 速ければ、たとえその瞬間の負荷が高くても、すぐにリソースを解放してくれるので問題ないし、複雑なことで負荷が限界を超えるなら、その処理でディスクソートが入るということになりますから、極端に遅くなるためすぐに分かります。(その処理が行われるセッションだけ、動的に Sort_Buffer_Size を大きくしても良いのですが、ご利用は計画的に!)

 つまり、書いてはいけないほど複雑かどうかは、「メンテできるか」を除けば、「速いかどうか」で判断すればよい。「メンテできるか」は要するに要員のスキルの問題ですから、RDBMSを使いながら(APIを使わずに)SQLを避けて得られるメリットは、「下手糞に合わせる」以外にないということになる訳です。

 くれぐれも、プロなら、「遅いけれどサーバの負荷は下がる」という世迷い言はいわないように!

感想

 今回、20年ぶりぐらいに「艦これ」と「Clash of Clans」と2つのゲームをやってみました。
 私はソーシャル系のシステムは未経験なので、実際、どれぐらいの負荷が掛かっているかは、外から見たシステムの状況から理論値で予想するしかありません。

 「Clash of Clans」も、それなりに同時ユーザがいてそうで、システムの複雑さ、そのパフォーマンスと安定性をみると、Flashゲームというハンデを差し引いても、「Clash of Clans」の方が何倍も上ですね。

 尤も、「Clash of Clans」は何らかの NoSQL(多分、MongoDBでしょう)を使っているはずですが(苦笑)

 クライアント処理のアニメーションなんて、レスポンスが遅いことの隠れ蓑(スプラッシュ画面と同じ)ですからね。「艦これ」なら数秒は余裕がある。
 最近のハードウェア環境で、「艦これ」の状態が良いときのレスポンスで良いなら、10万同時アクセスぐらい捌けそうなものなんですけどね……。

 もちろん、「艦これ」がこんな幼稚な理由でトラブっているのではないと信じていますけれど、MySQL を使いながら SQL を避けたときに起きる挙動に似てるな〜。とは、個人的に思っています。(違うことを祈る!)

 とにかく、RDBMS は SQL が最大のボトルネックになるのです。

 SQLを使わないことでそのボトルネックを避けてるつもりでも、NoSQL の API を使わない限り、必ず最大のボトルネックの SQL を通ってしまいますから、RDBMS を使うなら、シビアなシステムになればなるほど、SQL を極めなければいけない。

 年々、RDBMS を使いながら SQL を避ける人が増えていますが、SQL を使いきれば数倍〜数十倍のレスポンスが出せる場合がほとんどです。ということは、難しく考えなくても、同じハードウェア環境で数倍〜数十倍のアクセスに耐えられる。

 実に単純なことなんですけどね……。