みなさんこんにちは。GS2の丹羽です。
テックコラム第二回です。
今回はゲームサーバで使われるデータベースの話です。
前回
データベースの種類
データベースと一言で言ってもいろいろな種類があります。
基本的にはデータベースに格納されたデータを使う頻度でどこに保存するのかを決定します。
読み出し速度の早い順に
- メモリ
- キャッシュサーバ
- データベースサーバー
- ファイルサーバ
の順番で存在します。
メモリ
メモリはサーバに搭載されたメモリにデータを保管して読み出して使用します。
別の言い方をするとプログラムの変数に格納されたデータを使用する。ということになります。
この方法のデメリットは、負荷が増えてサーバを増やしたい。と思ったときにサーバによって空間が分離してしまうことです。
無秩序にメモリをデータベースとして使用してしまうと容易にサーバを増やすことが出来なくなってしまいます。
また、サーバはいつ止まるかわからないものです。そのため、このデータはいつ消えてもおかしくないデータを保管することにしか使えません。
これらの制約から一般的に一次キャッシュかより書き込みコストが高いデータベースに書き込むデータを一時的に溜めるバッファとして使用します。
キャッシュサーバ
名前の通りデータをキャッシュするためのデータベースです。
具体的なプロダクトとして memcached や redis といったプロダクトが使用されます。
これらのデータはキー値と値を保存するためのデータベースであり、構造化されたデータを保存するのには向きません。
また、プロダクトによってはデータが永続化されません。
そのため、キャッシュサーバも一時的なデータ置き場となります。
メモリと比較してネットワーク越しにアクセスするためアクセスするコストは高いですが、メモリに保管されたデータにアクセスして応答するだけのプロダクトですので、ほぼネットワークアクセスにかかる時間だけでデータを読み書きできます。
さらに、異なるサーバ間で同じキャッシュサーバを利用することでゲームサーバが増えてもキャッシュが分断することは無くなります。
ユーザの最終ログイン時間のような最悪消えてもなんとかなりつつ、みんなが同じデータを参照してほしい場合などに使うといいでしょう。
データベースサーバ
データベースはデータを永続化することを目的としたサーバです。
データベースサーバには RDBMS と NoSQL の二種類が存在します。
RDBMS
リレーショナルデータベースと呼ばれるカテゴリで、データの関係性を定義してその関係性に基づいてデータを保存できます。
例えば、ユーザーデータを保存する領域と、課金データを保存する領域を定義して、課金データは誰が課金したか。という情報を保存したとしましょう。
こうしておけば、ユーザデータに記録された年齢データから年齢別の課金額の統計を簡単に計算させることができます。
データベースサーバからデータを取り出すときに使用する言語がSQLです。
SQLは複雑な条件の処理も記述できるため非常によく使われています。
NoSQL
名前の通り SQL でデータにアクセスすることが出来ないデータベースです。
SQL は非常に柔軟にデータにアクセスできる一方で、それを実現するにはデータの柔軟性や速度やスケーラビリティを犠牲にしている部分があります。
NoSQL には主に2種類のアプローチが存在します。
データの柔軟性
速度やスケーラビリティ
NoSQL はこのどちらか、または両方を実現する目的で作られています。
スキーマレス
リレーショナルデータベースの特徴として、データの関係性を定義するという話をしました。
そのためにデータを保存する前にデータの構造(スキーマ)をデータベースに登録しておく必要があります。
たとえば、ユーザデータというものがあり、ユーザデータは数値の年齢という情報をもつ。というようなものです。
ゲームでいえばコンフィグの設定値の内容を保存したいケースを想像してください。
コンフィグはゲームの開発を進める過程で項目が増減します。そのたびにデータベースの設定も変更する。というのは少し非効率です。
そのため、事前にデータベースに保存するためのデータの構造を登録する必要がないデータベースが存在します。
このようなデータベースをスキーマレスデータベースと呼びます。
KVS
たとえば、ユーザデータと課金データを混ぜた統計情報が取れるということは、これらのデータが同一データベースに存在することを期待しています。
さらに、ユーザデータの年齢別の課金額の統計を出すためには、ユーザデータの年齢がどうなっているかを参照して、集計していかなければなりません。
ユーザーデータが1億人分存在することを想像してみてください。
集計処理をするためには、少なくともこのすべてのデータを参照していかなければ目的のデータを集計することができないのです。
実際にはインデックスと言われる機能で事前に年齢でソートしたデータを作っておいて、全てのデータを調べずに高速に目的のデータにアクセスできるようにするなどの工夫がされています。しかし、そのような遅いデータアクセスは根本的できないようにすることで高速化を図ったのが KVS です。
KVS はキーバリューストアの略で、キー値と値を保存することに特化したデータベースです。
キャッシュサーバーでも似たような話をしました。実際、Redisは永続化機能を持っているため、KVSとしても利用できます。
KVSのいいところは、データ間の関係を疎結合にすることで、データを分散して保存できることです。
たとえば、キー値のハッシュ値を100で割った値が何になるか。という情報を使って100台のサーバに分散して配置したとしましょう。
データのアクセスはキー値を使ったアクセスしか出来ませんので、キー値さえわかっていればどのサーバに値データが保存されているかを特定することができます。
この特性を利用すれば容易に負荷分散ができるというわけです。
実際は分散するサーバの数を途中で変更できるようにしたりと工夫があります。とにかく、KVSの魅力はスケーラビリティにあります。
デメリットはこの説明からもわかるようにキー値による値アクセスしかできません。
たとえばユーザデータはユーザIDをキー値として保存しているのであれば、年齢を使ってユーザの一覧をとるようなことは出来ません。
年齢を使うのをあきらめるか、ほかのデータベースを併用して目的を達成する必要があります。
ファイルサーバ
データベースを使わない方法としてファイルサーバにデータを置くという方法があります。
データベースサーバの場合データベースサーバーのメモリ上にデータがあればHDDやSSDのような低速な記憶領域にアクセスすることなくデータを取り出せますが、この方法ではそれは期待できません。(実際はOSがキャッシュしてくれてることがあります)
なのになぜこんな方法を使うのか?というと、サイズが大きなデータを取り扱うときにデータベースより使い勝手がいいためです。
たとえば、ゲームで言えばプレイヤーアイコンの画像データです。
データベースにはどのアイコンを使用しているかというID情報や画像データのURLを記録し、実際の画像データはIDやURLを使用して取得する。という形です。
Game Server Services ではどうやってるの?
GS2での実例を紹介します。
GS2ではスケーラビリティと可用性を最も重視しています。
ゲームサーバは世の中にあるサーバ類の中では多くのアクセスを受ける部類に入ります。さらに、事前にどれだけのアクセスが来るかを見積もるのは困難です。そのため、どれだけのアクセスが来ても耐えられるように設計しなければなりません。
努力の甲斐もあり、現在は1秒間に10万アクセス以上を処理できています。
単一のサーバ性能に縛られる設計ではこのような結果は得られません。
単一のサーバで処理できるデータベース処理はせいぜい1秒間に数百から数千アクセスだからです。
そのため、GS2では単一サーバ性能に縛られない…分散処理が出来ることを前提に設計しています。
メモリ
GS2ではまずはキャッシュとして メモリ を使用しています。
キャッシュサーバを利用しないの?と思うかもしれません。データがいつ消えてもいいキャッシュ用途であればわざわざサーバを用意するまでもない。逆にメモリであれば負荷分散出来る。というのがGS2の考えです。
キャッシュサーバが挟まることで、キャッシュサーバの性能にアクセス数の上限が縛られたり、サーバがダウンしたときの影響範囲が大きくなるため、使用していません。
(よく考えずにキャッシュサーバは使わなくていいんだ!という結論に至るのはやめてください。地獄を見ます)
データベース
次にデータベースですが、ここには KVS を使用しています。
具体的には AWS の DynamoDB と GCP の Cloud DataStore です。
これらのサーバは特性が異なるため使い分けていますが、共通点として正しい使い方をすればAWSやGCPのインフラの許す限りスケールするということです。
自分でKVSのサーバを立ててクラスタを組んだりすると自分が用意したクラスタのサイズに性能のリミットが設定されてしまいます。そのため、GS2ではクラウドベンダーが用意したKVSを活用しています。
ファイルサーバ
最後にファイルサーバですが、こちらも AWS の S3 と GCP の GCS を使用しています。
理由はデータベースで述べたのと概ね同じです。
しかし、ファイルサーバを使う最大の理由はデータにアクセスするために必要な料金がデータベースより安いためです。
GS2ではアクセス頻度やキャッシュヒット率を鑑みてファイルサーバを使うことで低コスト化を実現しています。
このように、データベースと言ってもいろいろな種類があり、用途によって使い分けることが重要となります。
そして、ゲームのリリース時にサーバがダウンする理由の大半がこのデータベースにあります。
データベースを正しく設計できていない場合、サーバを増やしたくても増やすことが出来ずに、トラブルを収束させることが困難になります。
おまけ
データベースサーバには実はもっと種類があります。
たとえばアクセス数の変化のような時系列データを保存することに特化したデータベースや、変更履歴を保存することに特化したデータベース。
ユーザ間の繋がりのようなデータを保存することに特化したグラフデータベースなんてのもあります。
ブロックチェーンなんかはデータの変更履歴を保存しつつ、さらに改ざんされにくくするものですね。
用途にあわせて適切なデータベースを選択しましょう!
GS2のご紹介
Game Server Services では ゲームサーバーのレンタルをおこなっています。
個人からプロまで使えるゲームサーバー - Game Server Services(GS2)
それでは、また。