ミクシィのサーバーエンジニアの業務

上平竜也氏(以下、上平):今日は私から「開発の裏側~サーバーサイド編~」ということでお話しします。お願いします。まず私がサーバーエンジニアとしてどんな仕事をしているかをざっくり紹介して、そのあとに、そのうちの案件の1つの「未開の大地」の拡張がどういったものだったかを説明します。

サーバーエンジニアは、アプリコードからインフラまで裏側全体を幅広く担当しています。その中でも私はアプリコードを担当することが多いです。そのうち、実際に担当してきた案件をいくつか紹介していきます。

まずは新機能開発の案件について。新しいコンテンツが入る際の、サーバー対応の実装です。例えば「トレジャー9」というイベントがあります。これは特定のクエストをクリアした時にパネルが出てきて、その指定したパネルに応じてアイテムをもらえるというコンテンツです。

他には、各種コラボイベントも新機能の開発です。版権との兼ね合いがあるので、具体的なIP名は出せません。IPに応じたイベントがコラボの度に開催されるので、その機能の開発を担当したりしていました。

次に既存のコンテンツの改良。例えばストックの機能の拡張。モンストにはストック機能というものがあって、特定の時間だけ遊べるクエストが多くあります。そのクエストをストックしておいて、好きな時に遊べるようにする機能があります。

このストック機能を改良して、ストックの消費タイミングをクエストの開始からクエストをクリアするまでに変更したり、あとは特定の条件で複数のストックを持てるようにする対応を行いました。

このあと詳しく話をしますが、「未開の大地」というコンテンツの階層拡張もこれにあたりますね。

他には企画さんやエンジニアが開発作業で使うツールの改修をしたり、インフラまわりの仕事で、データベースやインスタンスなど、クラウドまわりのお世話もしたりしています。

「未開の大地」とは?

ここからは、先ほど紹介した「未開の大地」の拡張について話をしていきます。そもそも「未開の大地」がどういうコンテンツかについてですが、これは毎月定期的に開催されるイベントで、最初は1階から10階まで挑戦することができます。それらをすべてクリアすると、次の月に開催されるイベントでは2階から11階まで遊ぶことができるようになります。そしてそれらもすべてクリアすると、(次は)3階から12階まで遊べるといったように、どんどん挑戦できる階層が上がっていくイベントです。

このイベントは2年以上開催されていて、現在は34階まで遊ぶことができますが、企画当初の段階では、30階で終了する予定のコンテンツでした。ただ、プレイ数やユーザーの反応がよくて、30階以降も開催していこうとなりました。

「未開の大地」のクリアデータ管理方法の問題

ただ31階以降を続けていくには、ちょっとサーバー側の問題がありました。データベースに32階までのクリア情報しか保存できないという問題があって。なぜ32階までしか保存ができなかったかというと、クリアデータの管理方法が理由です。

「モンスト(モンスターストライク)」のクリア情報を管理しているテーブルです。モンストでは、クリア情報をステージのグループ単位で管理をしています。

このステージのグループがどういうものかですが、モンストでは1つの敵に対して複数の難易度が用意されていて、そのステージを集めたものがグループになります。

この例でいうと、プリンセス・ノッコという敵がいて、極・究極がそれぞれステージにあたり、それらを合わせたものがステージグループになります。そしてこのクリア情報管理テーブルでは、Completeカラムというものが、実際にどのステージをクリアしたかをビットで保存をしています。この例でいうと、極ステージが1ビット目、究極ステージが2ビット目といった感じです。

「未開の大地」もこのテーブルでクリア情報を管理していて、当初30階まであったステージはすべて1つのステージグループに紐づいています。そのため、Completeカラムには、30階までそれぞれのクリア情報が入ってました。

そしてビット管理をしているCompleteカラムですが、32ビットの型で、それが理由で33個以上の情報を保存することができません。

なので、すべてのステージが1つのステージグループに紐づいている「未開の大地」は、ステージグループに33個以上紐づけられないという理由で、32階までしか設定できませんでした。「未開の大地」を拡張していくにあたっては、この問題を解決する必要がありました。

「未開の大地」拡張の解決策

この問題に対していくつか解決案があって、そのうち大きなものが2つ上がりました。

1つがクリア情報テーブルのカラムを拡張していって、32より多くのステージ情報を持てるようにする方法。そしてもう1つが、複数のレコードを使って「未開の大地」の情報を管理する方法です。

この2つのうち単純な解決案はカラムを拡張する方法。例えば、今のCompleteカラム32ビットのところを64ビットにするなどですね。これができれば、カラム拡張以外に特に対応は必要なくなるので、簡潔に終わらせられます。

ただ、この方法は実際に使うことはできませんでした。クリア情報を管理しているテーブルは「未開の大地」以外にすべてのステージのクリア情報を持っていて、1ユーザーに対して非常に多くのレコードが存在します。

そしてレコード数が非常に多いテーブルの構成を変えようと思うと、その処理に非常に時間がかかってしまいます。モンストではカラムを追加したりする処理はメンテナンスタイミングで行っているんですけれど、メンテナンス内に終わらない処理は基本的にできなくて、今回もメンテ内では間に合わないだろうということで、この方法は見送りになりました。

そこで、今回は複数のステージグループ情報を使って「未開の大地」のクリア情報を管理する方法を使うことになりました。図にするとこういう管理です。「未開の大地」だけ複数のレコードを使ってクリア情報を管理する感じです。

複数のレコードを跨いだ情報確認の問題

複数レコードでクリア情報を管理することにしましたが、これにも問題点がありました。「未開の大地」を制覇、つまりその月にすべての階層をクリアしているかを確認するには、複数のレコードを跨いで確認する必要があります。

例えば、30階から39階まで挑戦可能なユーザーがいた時に、前のレコードを3ビット、後ろのレコードを7ビット見る必要があるといった感じです。

クリア情報をリセットする処理が各レコード単体に発生してしまうため、実際には制覇しているのに片方だけリセットが入ってしまい、制覇しているのに制覇していないような扱いになってしまう場面がどうしても出てきてしまいました。この問題も何とかして解決する必要がありました。

そこで、制覇済みかを各ステージがクリアしたかどうかをCompleteカラムを見にいって逐一チェックしていくのではなく、クリア情報を管理するテーブルとは別のところに、「未開の大地」を制覇しているかどうかを示すカラムを追加することにしました。例えば、ユーザーが1から10階まで挑戦できる時に、10階をクリアした場合に、制覇済みかどうかのカラムに制覇したことを保存していくといった感じです。

ただ、カラムの追加にもちょっとややこしい問題があります。カラムの追加処理をした時にはすべてデフォルトの値が入っており、それで制覇していないことになります。

しかし、本番環境ではすでに「未開の大地」を遊んでくださっているユーザーがたくさんいて、その中で制覇済みの方もたくさんいるはずなので、デフォルトの値が「制覇済みでない」ことになっているんですけれど、実際には制覇済みの方もたくさんいる不整合が発生していることになります。

この状態で新しい管理方式を適用しても、制覇済みのカラムもすべて「制覇していない」という値が入っているために問題が発生してしまうので、これも解決する必要がありました。

そこで、ここでは本番環境にスクリプトを適用して、クリア情報管理テーブルのCompleteカラムの値を見て、すでに制覇しているユーザーの制覇済みカラムを「制覇した」と更新する処理を適用していきました。

他には、このクリア情報を管理するためのCompleteカラムは既存のいろいろな処理で使われていたので、そういった処理に影響がないかを確認していく必要がありました。例えば、全クリア報酬の処理。すべての難易度をクリアしたらオーブがもらえるようなクエストがたくさんあると思いますが、そういった処理に影響が出ないかとか。

あとは、クリア情報を参照するようなミッションが今回の処理に入った時に、適切に動作するかです。これらを調査して問題がなさそうかを確認していく。もし問題がありそうだったら、それを適宜修正をしていきながら実装を進めていきました。

拡張処理に対して必要な処理

(スライドを示して)最終的に拡張処理に対して必要な処理はこんな感じになりました。まずはメンテナンス中にカラムの追加作業をして、制覇済みカラムを用意していきます。そして、「未開の大地」が開催されていないタイミングでスクリプトを適用して、制覇済みかの情報を、先ほど用意したカラムに移していきます。さらに次のメンテナンスタイミングで、「未開の大地」を新しい管理方式の複数レコードで管理をする方式に適用をして、無事に拡張ができるようになりました。

最後に余談ですが、こういった(ような)「最初はここまでやる」という予定だったものが、「やっぱり続けていきたいな」となることは、ゲームを運営していく上ではけっこうあるあるなんですね。

今回はプレイ数だったりやユーザーの反応がよくて31階以降も続けていこうということになったので、それによって追加対応、拡張の対応が必要になっりましたが、ユーザーに喜んでもらえだから理由での対応だったので、それはそれでうれしく楽しく対応することができました。

私からの話は以上になります。ありがとうございました。

「覇者の塔」のステージグループの状況

司会者:ありがとうございます。それではここでQ&Aに移りたいと思います。ここからは王さんにも参加いただいて、みなさまの質問に回答していければと思います。どうぞよろしくお願いします。

王奇氏(以下、王):よろしくお願いします。

上平:お願いします。

司会者:さっそく質問をいただいているので、回答していこうかなと思います。「『未開の大地』が33階以上の問題でしたが、『覇者の塔』の時はどのように解決されていましたか。あるいは、『覇者の塔』の場合は、最初から40階の予定だったのでしょうか」ということなんですけど。開発に携わられたのは?

:僕からじゃあ。

司会者:ぜひぜひ。

:そうですね。「覇者の塔」はそもそも33階の問題はなかったです。先ほどの中にステージグループの話がありましたが、「未開の大地」は1つのステージグループでやりくりしていたので30階(まで)みたいな制限がありました。しかし、「覇者の塔」は最初からすべての階層が1つのステージグループになっていたので、今だと40階、40ステージグループあります。

「覇者の塔」の制覇の報酬、「何階まで制覇したら何報酬なのか」は、すべて独自の実装になっています。「覇者の塔」の時なので、「1個のステージグループでいいじゃん」という話にもなるかもしれませんが、「当初そういう実装になった」というただの事実もあります。「未開の大地」を実装した時は、「1人のユーザーが40レコード分できるので、あまり効率はよくないですよ」という話もありました。

30階までやるのであればステージグループは1個で事足りるので、そっちのほうがエコじゃないかと。そういう側面もあります。だから「未開の大地」は昔の例をそのまま流用(するの)ではなくて、独自に「こっちのほうがいいんじゃないですか」みたいな感じでチャレンジしてみました。その結果が今ですね(笑)。

司会者:なるほど。「覇者の塔」とは別にチャレンジしたのが「未開の大地」で、そもそも(「覇者の塔」で同じことが起きなかったのは)「覇者の塔」が1個ずつがグループになっていたからという。ぜんぜん作り方が違ったと。

:そういうことです。

司会者:わかりました。回答になってるでしょうか? ご確認いただければと思います。どしどし質問していただいていますので、このように答えていければなと思います。聞きたいことがあればオンラインなので、ぜひ発信してもらえればと思います。

ユーザーの声から改良を重ねた事例

司会者:今本編の最後の余談のところで「ユーザーさんの声もあってうれしい改良だった」というお話をしてもらいましたが、(モンストは)長く運用しているタイトルなので、ユーザーさんの声があったり反響がよかったりして、最初の実装から改良を重ねているようなところでいうと、他に事例とかはありますか?

上平:そうですね。最近のもので僕の担当したところでいうと、コラボ内容の具体的なIPは言えませんが、以前、経験値をたくさんもらえるイベントをコラボでさせていただいて。それはもともとはコラボだけで終わらせる予定でしたが、すごくユーザーに好評だったので、もうちょっと汎用化して、モンストとしてのコンテンツになる“けいウサ”というキャラが現れたりということあったりしました。

司会者:なるほど。コラボで好評で、実際にモンストの機能にということで、“けいウサ”ちゃんができましたと。なるほど。それもユーザーさんの声があってなんですね。

上平:そうですね。

司会者:そうなんですね。今までのメソッドとかもそうですがけっこうユーザーさんの声とか反響がよくて企画が上がるようなこともあったりするので、本当にみなさんに支えてもらっていますね。

(一同笑)

モンストで使用している言語

司会者:ありがとうございます。新しい質問が来ました。「モンストの開発で使っているプログラミング言語はC関係ですか?」。

上平:メインのサーバーはRubyという言語で動いていて、あとは裏側だとGoですかね。

:そうですね。ツールはGolangでやっています。監視まわりで歴史があるものだとPerlですね。

司会者:おぉー。

:知っている方もいらっしゃるかもしれませんが、SNSのmixiはPerlなんですよ。

司会者:なるほど。

:わりかし昔のシステムを導入する際に、Perlのツールがあったりします。

司会者:けっこう老舗の。

:今はだんだんなくなっていますが。

司会者:じゃあRubyとかGoとか。

:そうですね。おしゃれなGolangを起用していきました。

司会者:なるほど。クライアント……。

:C++ですね。

司会者:C++か。開発に使っているというところでいうと、クライアントだとC関係。

上平:サーバー側だとRubyがメインですね。

:どちらかというとRailsっぽい何か。

上平:Railsっぽい何か(笑)。Railsではないけれど、Railsライク。

:そうなんですよ。サーバーチームはRailsライクのフレームワークを作っていますね。Padrinoというやつなんですけど。

司会者:(質問者に対して)大丈夫そうですか? クライアントはC関係を使っているかもしれないけれど、サーバーはRubyとかGoとか、古いものでPerlとかですね。

:そうですね。

サーバーエンジニアはサービス全体に関わる業務も実施しているか?

司会者:ありがとうございます。(質問は)順番に紹介しますね。「サーバーサイド編ということで、ユーザーのアクセス増加によるハードなどの負荷処理などの、サービス全体に関わる部分の業務なども担当されているのでしょうか」。

上平:サーバー側はサーバーサイドエンジニアが担当していて、僕がメインではないので(笑)。

:でも上平さんもやっていることはやっていると思いますよ。

上平:ハードまわりは(やっています)。ソフトウェアの(中で)いわゆる効率のいいプログラムを書いたりすることは僕の担当ですが、ハードまわりになってくるとインフラまわりが中心になってくるので、そこまでちょっと詳しくしゃべれないですね。

:上平さんはラッキングはやりませんでしたっけ?

上平:やりました。入った最初の頃にデータセンターに行って、ラックのガシャンガシャン(はめること)はやりました。

司会者:へー。

:一応そういうツアーがあって。

司会者:おもしろい。

:サーバーグループは何でもやる方針で、人によってやることの偏りはありますが、大抵はやっていますね。

司会者:サービス全体に関わるというか、負荷分散をやっているメンバーの方もいる。

:そうですね。ハードによる負荷処理で言うと、年末とかになるとサーバーを購入したりしますね(笑)。

司会者:年末年始は本当にたくさんのユーザーさんに遊んでもらっているので。

:そういう感じのことも人によっては担当したりしますね。

司会者:そうですね。サービス全体に関わることはみんなで分担して、サーバーのチームのみなさんにやってもらったりする感じですね。

:そうですね。最近ちょうどそういう感じのことが行われました。新しい機能が出て負荷が集中すると、バックエンドのデータベースを分散するような仕事も一応サーバーグループが担当する分野ではありますので、もともと1台のやつを複数台に分散するようなことを、物理的にもロジック的にもやったりします。

司会者:サーバーやインフラのようなところは幅広くやっている感じですね。ありがとうございます。

モンストの開発にRubyを使用している理由

次の質問で「ゲーム開発でRubyはあまり聞かないのですが、選んだ理由はありますか?」。

上平:これは歴史的経緯が(笑)。

司会者:あるー。王さんはわかりますか?

:……えっと。

司会者:(笑)。

:ないです!

(一同笑)

:そもそも「Railsライクのものは初期は開発しやすい」ということがあるかもしれないんですけれど、大きな選定要因は、初期にアーキテクチャというか設計した人の好み(笑)。独断と偏見で。

司会者:歴史(笑)。

上平:選んだ理由ではなく「あまりゲーム開発では聞かないんですか」とありましたが、僕が新卒で入った前の会社も同じくソーシャルゲームの開発をしていましたが、そこでもRubyを使っていましたね。

司会者:へー。

上平:多いかというとそんなことはないかと思いますが、少ないわけでもないのかな。たぶん、これから新しく開発されていくゲーム(の中)で(は)、Rubyがちょっと減っているのかなとは思います。

司会者:なるほど。どうなんでしょうかね。

:1つの要因として、モンストは具体的にゲームロジックをサーバーで実装していなくて。いわゆるゲームサーバーみたいなものは実は存在しないんです。モンストのサーバーの大きな部分は、いわゆるHTTPベースのWebサーバーになっていて、そういう意味でRubyに適正がありました。

司会者:なるほど。それはSNS mixiをやっていた流れもあったりするんですか?

:そういう流れもあるし、あとはゲームの特徴としてそういうことができる部分もあります。できないゲームももちろんあったりして。リアルタイムでアクションをやるようなゲームだと、接続を張りっぱなしで通信するような感じになるので、独自ソケットなどになると、「Webサーバーが適正ではないよね」という感じになって、Rubyの出番がなくなります。

司会者:なるほど。ゲームの作りというか、どういうゲームを作るかで選択するものが変わってくる。

:そういう部分もありますね。

司会者:わかりました。ありがとうございます。

(次回に続く)