SlideShare a Scribd company logo
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ゲームエンジニアのための
データベース設計
株式会社 DeNA Games Osaka
技術編成部
人西 聖樹 masaki.hitonishi@dena.com
Copyright © DeNA Co.,Ltd. All Rights Reserved.
自己紹介
 人西聖樹 (ひとにし まさき)
 株式会社 DeNA Games Osaka 2014年 入社
 Webアプリケーションエンジニア
 シューティングゲーム好き。東方Project 大好き
 某400万人ユーザー超えモバイルゲームの
開発やってます
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ゲームの
サーバーサイド
Copyright © DeNA Co.,Ltd. All Rights Reserved.
データを
どうやって
保存しようか?
Copyright © DeNA Co.,Ltd. All Rights Reserved.
多様な選択肢
 RDBMS
MySQL
Oracle
PostgreSQL
 KVS
Redis
Riak
 カラム指向型
Apache Cassandra
Hbase
 ドキュメント指向
MongoDB
Apache CouchDB
etc…
Copyright © DeNA Co.,Ltd. All Rights Reserved.
今日は MySQL の
話をします!
Copyright © DeNA Co.,Ltd. All Rights Reserved.
突然ですがアンケー
ト
Copyright © DeNA Co.,Ltd. All Rights Reserved.
MySQL使った事ある人!
Copyright © DeNA Co.,Ltd. All Rights Reserved.
explain コマンド
使ったことある人!
Copyright © DeNA Co.,Ltd. All Rights Reserved.
InnoDB における
ギャップロック と
ネクストキーロック
について説明できる人!
Copyright © DeNA Co.,Ltd. All Rights Reserved.
本日のテーマ
 RDBMS (リレーショナルデータベース) とは
 データベース観点でのゲームのデータの特徴
 データベース構築時に気をつけること
 アプリケーション開発時に気をつけること
Copyright © DeNA Co.,Ltd. All Rights Reserved.
リレーショナルデータベースとは
 データを行と列の組み合わせによる表で表す
 複数の表と表を関係(リレーション)によって組み合わせられる
ID 名前 攻撃力 防御力
1 Aさん 100 100
2 Bさん 200 150
ユーザーID アイテムID 所持数
1 1 1
1 2 3
1 3 5
ユーザーテーブル
アイテム所持テーブル
ユーザーテーブルの情報から
Aさんがどのアイテムを何個所持している
か取得することができる。
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ACID特性
 Atomicity 原子性
トランザクションの操作は全て実行されるか
まったく実行されないかのどちらか
 Consistency 一貫性
トランザクション開始時と終了時にデータの
整合性が保たれる
 Isolation 独立性
他のトランザクションによる操作の影響を受けない
 Durability 永続性
コミットしたトランザクションのデータは保存される
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ゲームデータの特徴
 ユーザーを primary key としたレコードが多い
 永続データと期間限定データ(イベントのデータ等)がある
 マスタデータ(read only)が多い
アイテムマスタ、ボスマスタ、ボス出現マスタ etc…
 レコードの状態更新が多い
ボスのHP減少、HP回復、マップ移動 etc…
 可用性・整合性は大切
ゲームがプレイできない、アイテムを使用したのに
回復していない等の不具合・障害に対して、
課金しているユーザーの温度感は非常に高い
Copyright © DeNA Co.,Ltd. All Rights Reserved.
データベース構築時に考えること
 Master/Slave 構成
 垂直分割
 水平分割
垂直/水平分割はアプリケーション側で対応しないといけない
(MySQL 側に仕組みがない)が後から追加するのは
大変なので最初から考慮して開発する
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Master / Slave 構成
Master
Slave Slave Slave
レプリケーション
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ゲームは更新系クエリがめちゃ多い
 ボタンを押すだけでステータス更新
 体力増減とか
 ボスへダメージとか
 アイテム獲得とか
Copyright © DeNA Co.,Ltd. All Rights Reserved.
 参照系クエリは Slave に逃せる。
 Slave のスケールアウトは容易
 更新系クエリは必ず Master にI/O負荷がか
かる → Master はボトルネックになりがち
Copyright © DeNA Co.,Ltd. All Rights Reserved.
垂直分割
Aテーブル
Bテーブル
Cテーブル
Dテーブル
Eテーブル
Fテーブル
Gテーブル
Hテーブル
Iテーブル
 テーブルの種類によって DB を分割。
 Join 句が使えなくなる。
 テーブルへのアクセス数が均等になるように分割しないと負荷が偏る
Copyright © DeNA Co.,Ltd. All Rights Reserved.
水平分割
 レコードのカラムの値でDBを分割
 範囲取得や count, sum が面倒に
 auto_increment が使えなくなる→採番テーブルを別途用意する
Aテーブル
Bテーブル
Cテーブル
Aテーブル
Bテーブル
Cテーブル
Aテーブル
Bテーブル
Cテーブル
↑ID: 1 のレコードはこっち
↑ID: 2 のレコードはこっち
↑ID: 3 のレコードはこっち
Copyright © DeNA Co.,Ltd. All Rights Reserved.
複数のトランザクションを扱う
 複数DB へのトランザクションをどう扱うか?
 InnoDB の REPEATABLE READ は最初のクエリ発行時に取得できるレ
コードの値が決定するので、各DBに対して最初のクエリをいつ投げる
か意識する必要がある。
 コミットタイミングは全て同一で行うのが楽
 コミットタイミングが別々だとデータ不整合が起こりやすくなる(片方
はコミット済みなのにもう片方はロールバックとか…
Copyright © DeNA Co.,Ltd. All Rights Reserved.
アプリケーション開発時に気をつけること
 適切なindexとindexを使える適切なクエリ
 クエリ発行量を減らす
 行ロック
 レプリ遅延対策
 Repeatable read の特性
Copyright © DeNA Co.,Ltd. All Rights Reserved.
インデックス
InnoDB のインデックスは B+ Tree
常に一定の深度になるようにバランス化された木構造
1レコードの取得に対して O(log N) で探索することができる
Copyright © DeNA Co.,Ltd. All Rights Reserved.
オプティマイザ
オプティマイザとは・・・
SQLがどのインデックスを使用し、どの順序でアクセスするかという
実行計画(EXPLAIN)を決定する
EXPLAIN構文・・・
「EXPLAIN SELECT~」とすることで、オプティマイザが
選択した実行計画を表示できる
UPDATEやDELETEの場合、SELECTに書き換える必要がある
mysql> explain select * from test where id = 1;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
| 1 | SIMPLE | test | const | PRIMARY | PRIMARY | 4 | const | 1 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
Copyright © DeNA Co.,Ltd. All Rights Reserved.
オプティマイザ
MySQLはコストベース・オプティマイザ
コストベースのオプティマイザでは、統計情報だけなく
CPUクロック
メモリ容量
DISK I/O速度
DBMSでのパラメータ
で実行計画が決定される
開発環境と同じ実行計画になるとは限らない
データ件数が違う
データの種類が違う
CPUクロックが違う
メモリ容量が違う
など
MySQL のオプティマイザは実行計画を結構見誤る
不安ならばFORCE INDEX で使用するインデックスを指定する
Copyright © DeNA Co.,Ltd. All Rights Reserved.
インデックスを使えないケース例
ALTER TABLE テーブル名 ADD KEY (col1, col2, col3);
・否定
WHERE col1 <> 1
・2つ目のキーから指定
WHERE col2 = 1 AND col3 = 1
・カラム側に計算式を使用
WHERE col1 * 100 = 100
・範囲指定
WHERE col1 > 1 AND col2 = 2 (col2はインデックスを使えない)
・昇順と降順の混在
ORDER BY col1 ASC, col2 DESC(col2はインデックスを使えない)
Copyright © DeNA Co.,Ltd. All Rights Reserved.
クエリ発行量を減らす
 綺麗に正規化しない(あえて冗長にデータを持つことで 1 query で必要
なデータを取得する)
 あえてカラムを分割する
更新が低いが参照の多いテーブルはmemcached にキャッシュする
更新が多いテーブルはなるべくカラムを絞って InnoDB の buffer
pool に乗るようにする
 時限付きデータ(イベントデータ等)は別テーブルにすることでイベント
終了後に drop table できるようにする
 IN句でSELECT
SELECT * FROM user WHERE id IN(1, 2, 3, ...);
 Bulk insert
INSERT INTO user values (1,'tanaka'),(2,'yamada'),(3,'hansen');
 INSERT INTO … ON DUPLICATE KEY UPDATE …
レコードが存在しなければ INSERT 、存在すれば UPDATE を 1
query で実行できる。
Copyright © DeNA Co.,Ltd. All Rights Reserved.
行ロック
 同時操作を常に意識する
AさんとBさんが同時にボスを攻撃したら両方ともボスを撃破した扱い
になったり…
Aさんが2端末使って、同時にアイテムを受け取りを押すことでアイテ
ム増殖できたり…
 前者は攻撃時にまずボスレコードをロックして、AさんとBさんの処理
を直列させることで防げる
 後者はアイテム受け取り時にAさんのレコードをロックして処理を直列
させることで防げる
 ロックの順番を統一しないと、デッドロックが発生する。
 必ず存在するレコードに対してロックを取る
 存在しないレコードをロックすると、InnoDBの Repeatable Read で
は gap lock が発生し、広範囲にロックを獲得する。
→ lock wait timeout
Copyright © DeNA Co.,Ltd. All Rights Reserved.
レプリケーション遅延対策
 大量クエリのコミット等でレプリ遅延(master DBへの変更が slave
DBへ反映が遅れること)が発生する
 回復アイテムの使用(Masterを更新)→次ページで使用結果を見ると回復
していない(Slave にまだ回復の反映が遅れてる)
→更新処理後、更新したデータをcacheに詰めて、遷移先で使用する
→更新処理後、更新処理から遷移されてきたかどうかを見て、master
or slave のどちらを参照するか決める
 1リクエスト内で Slave のデータを元に Master を更新すると、レプリ
遅延で古い Slave のデータを参照していてデータの不整合が起こる
→更新系のリクエスト内で参照するDB は Master で統一する
Copyright © DeNA Co.,Ltd. All Rights Reserved.
KVSとの併用について
 ゲームデータのキャッシュは難しい
 更新を頻繁に行うのでキャッシュクリア処理が面倒
 忘れると気づきづらい障害に
 トランザクションとの整合性
 Read Only のデータ(マスタ等)をキャッシュするのが一番楽
Copyright © DeNA Co.,Ltd. All Rights Reserved.
 ゲームサーバーのデータベースは整合性/負荷と
の戦い
 ノウハウを知って急激なアクセス増加にも耐えら
れる構築/開発をしよう
Copyright © DeNA Co.,Ltd. All Rights Reserved.
おわり

More Related Content

ゲームエンジニアのためのデータベース設計

  • 1. Copyright © DeNA Co.,Ltd. All Rights Reserved. ゲームエンジニアのための データベース設計 株式会社 DeNA Games Osaka 技術編成部 人西 聖樹 [email protected]
  • 2. Copyright © DeNA Co.,Ltd. All Rights Reserved. 自己紹介  人西聖樹 (ひとにし まさき)  株式会社 DeNA Games Osaka 2014年 入社  Webアプリケーションエンジニア  シューティングゲーム好き。東方Project 大好き  某400万人ユーザー超えモバイルゲームの 開発やってます
  • 3. Copyright © DeNA Co.,Ltd. All Rights Reserved. ゲームの サーバーサイド
  • 4. Copyright © DeNA Co.,Ltd. All Rights Reserved. データを どうやって 保存しようか?
  • 5. Copyright © DeNA Co.,Ltd. All Rights Reserved. 多様な選択肢  RDBMS MySQL Oracle PostgreSQL  KVS Redis Riak  カラム指向型 Apache Cassandra Hbase  ドキュメント指向 MongoDB Apache CouchDB etc…
  • 6. Copyright © DeNA Co.,Ltd. All Rights Reserved. 今日は MySQL の 話をします!
  • 7. Copyright © DeNA Co.,Ltd. All Rights Reserved. 突然ですがアンケー ト
  • 8. Copyright © DeNA Co.,Ltd. All Rights Reserved. MySQL使った事ある人!
  • 9. Copyright © DeNA Co.,Ltd. All Rights Reserved. explain コマンド 使ったことある人!
  • 10. Copyright © DeNA Co.,Ltd. All Rights Reserved. InnoDB における ギャップロック と ネクストキーロック について説明できる人!
  • 11. Copyright © DeNA Co.,Ltd. All Rights Reserved. 本日のテーマ  RDBMS (リレーショナルデータベース) とは  データベース観点でのゲームのデータの特徴  データベース構築時に気をつけること  アプリケーション開発時に気をつけること
  • 12. Copyright © DeNA Co.,Ltd. All Rights Reserved. リレーショナルデータベースとは  データを行と列の組み合わせによる表で表す  複数の表と表を関係(リレーション)によって組み合わせられる ID 名前 攻撃力 防御力 1 Aさん 100 100 2 Bさん 200 150 ユーザーID アイテムID 所持数 1 1 1 1 2 3 1 3 5 ユーザーテーブル アイテム所持テーブル ユーザーテーブルの情報から Aさんがどのアイテムを何個所持している か取得することができる。
  • 13. Copyright © DeNA Co.,Ltd. All Rights Reserved. ACID特性  Atomicity 原子性 トランザクションの操作は全て実行されるか まったく実行されないかのどちらか  Consistency 一貫性 トランザクション開始時と終了時にデータの 整合性が保たれる  Isolation 独立性 他のトランザクションによる操作の影響を受けない  Durability 永続性 コミットしたトランザクションのデータは保存される
  • 14. Copyright © DeNA Co.,Ltd. All Rights Reserved. ゲームデータの特徴  ユーザーを primary key としたレコードが多い  永続データと期間限定データ(イベントのデータ等)がある  マスタデータ(read only)が多い アイテムマスタ、ボスマスタ、ボス出現マスタ etc…  レコードの状態更新が多い ボスのHP減少、HP回復、マップ移動 etc…  可用性・整合性は大切 ゲームがプレイできない、アイテムを使用したのに 回復していない等の不具合・障害に対して、 課金しているユーザーの温度感は非常に高い
  • 15. Copyright © DeNA Co.,Ltd. All Rights Reserved. データベース構築時に考えること  Master/Slave 構成  垂直分割  水平分割 垂直/水平分割はアプリケーション側で対応しないといけない (MySQL 側に仕組みがない)が後から追加するのは 大変なので最初から考慮して開発する
  • 16. Copyright © DeNA Co.,Ltd. All Rights Reserved. Master / Slave 構成 Master Slave Slave Slave レプリケーション
  • 17. Copyright © DeNA Co.,Ltd. All Rights Reserved. ゲームは更新系クエリがめちゃ多い  ボタンを押すだけでステータス更新  体力増減とか  ボスへダメージとか  アイテム獲得とか
  • 18. Copyright © DeNA Co.,Ltd. All Rights Reserved.  参照系クエリは Slave に逃せる。  Slave のスケールアウトは容易  更新系クエリは必ず Master にI/O負荷がか かる → Master はボトルネックになりがち
  • 19. Copyright © DeNA Co.,Ltd. All Rights Reserved. 垂直分割 Aテーブル Bテーブル Cテーブル Dテーブル Eテーブル Fテーブル Gテーブル Hテーブル Iテーブル  テーブルの種類によって DB を分割。  Join 句が使えなくなる。  テーブルへのアクセス数が均等になるように分割しないと負荷が偏る
  • 20. Copyright © DeNA Co.,Ltd. All Rights Reserved. 水平分割  レコードのカラムの値でDBを分割  範囲取得や count, sum が面倒に  auto_increment が使えなくなる→採番テーブルを別途用意する Aテーブル Bテーブル Cテーブル Aテーブル Bテーブル Cテーブル Aテーブル Bテーブル Cテーブル ↑ID: 1 のレコードはこっち ↑ID: 2 のレコードはこっち ↑ID: 3 のレコードはこっち
  • 21. Copyright © DeNA Co.,Ltd. All Rights Reserved. 複数のトランザクションを扱う  複数DB へのトランザクションをどう扱うか?  InnoDB の REPEATABLE READ は最初のクエリ発行時に取得できるレ コードの値が決定するので、各DBに対して最初のクエリをいつ投げる か意識する必要がある。  コミットタイミングは全て同一で行うのが楽  コミットタイミングが別々だとデータ不整合が起こりやすくなる(片方 はコミット済みなのにもう片方はロールバックとか…
  • 22. Copyright © DeNA Co.,Ltd. All Rights Reserved. アプリケーション開発時に気をつけること  適切なindexとindexを使える適切なクエリ  クエリ発行量を減らす  行ロック  レプリ遅延対策  Repeatable read の特性
  • 23. Copyright © DeNA Co.,Ltd. All Rights Reserved. インデックス InnoDB のインデックスは B+ Tree 常に一定の深度になるようにバランス化された木構造 1レコードの取得に対して O(log N) で探索することができる
  • 24. Copyright © DeNA Co.,Ltd. All Rights Reserved. オプティマイザ オプティマイザとは・・・ SQLがどのインデックスを使用し、どの順序でアクセスするかという 実行計画(EXPLAIN)を決定する EXPLAIN構文・・・ 「EXPLAIN SELECT~」とすることで、オプティマイザが 選択した実行計画を表示できる UPDATEやDELETEの場合、SELECTに書き換える必要がある mysql> explain select * from test where id = 1; +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+ | 1 | SIMPLE | test | const | PRIMARY | PRIMARY | 4 | const | 1 | Using index | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
  • 25. Copyright © DeNA Co.,Ltd. All Rights Reserved. オプティマイザ MySQLはコストベース・オプティマイザ コストベースのオプティマイザでは、統計情報だけなく CPUクロック メモリ容量 DISK I/O速度 DBMSでのパラメータ で実行計画が決定される 開発環境と同じ実行計画になるとは限らない データ件数が違う データの種類が違う CPUクロックが違う メモリ容量が違う など MySQL のオプティマイザは実行計画を結構見誤る 不安ならばFORCE INDEX で使用するインデックスを指定する
  • 26. Copyright © DeNA Co.,Ltd. All Rights Reserved. インデックスを使えないケース例 ALTER TABLE テーブル名 ADD KEY (col1, col2, col3); ・否定 WHERE col1 <> 1 ・2つ目のキーから指定 WHERE col2 = 1 AND col3 = 1 ・カラム側に計算式を使用 WHERE col1 * 100 = 100 ・範囲指定 WHERE col1 > 1 AND col2 = 2 (col2はインデックスを使えない) ・昇順と降順の混在 ORDER BY col1 ASC, col2 DESC(col2はインデックスを使えない)
  • 27. Copyright © DeNA Co.,Ltd. All Rights Reserved. クエリ発行量を減らす  綺麗に正規化しない(あえて冗長にデータを持つことで 1 query で必要 なデータを取得する)  あえてカラムを分割する 更新が低いが参照の多いテーブルはmemcached にキャッシュする 更新が多いテーブルはなるべくカラムを絞って InnoDB の buffer pool に乗るようにする  時限付きデータ(イベントデータ等)は別テーブルにすることでイベント 終了後に drop table できるようにする  IN句でSELECT SELECT * FROM user WHERE id IN(1, 2, 3, ...);  Bulk insert INSERT INTO user values (1,'tanaka'),(2,'yamada'),(3,'hansen');  INSERT INTO … ON DUPLICATE KEY UPDATE … レコードが存在しなければ INSERT 、存在すれば UPDATE を 1 query で実行できる。
  • 28. Copyright © DeNA Co.,Ltd. All Rights Reserved. 行ロック  同時操作を常に意識する AさんとBさんが同時にボスを攻撃したら両方ともボスを撃破した扱い になったり… Aさんが2端末使って、同時にアイテムを受け取りを押すことでアイテ ム増殖できたり…  前者は攻撃時にまずボスレコードをロックして、AさんとBさんの処理 を直列させることで防げる  後者はアイテム受け取り時にAさんのレコードをロックして処理を直列 させることで防げる  ロックの順番を統一しないと、デッドロックが発生する。  必ず存在するレコードに対してロックを取る  存在しないレコードをロックすると、InnoDBの Repeatable Read で は gap lock が発生し、広範囲にロックを獲得する。 → lock wait timeout
  • 29. Copyright © DeNA Co.,Ltd. All Rights Reserved. レプリケーション遅延対策  大量クエリのコミット等でレプリ遅延(master DBへの変更が slave DBへ反映が遅れること)が発生する  回復アイテムの使用(Masterを更新)→次ページで使用結果を見ると回復 していない(Slave にまだ回復の反映が遅れてる) →更新処理後、更新したデータをcacheに詰めて、遷移先で使用する →更新処理後、更新処理から遷移されてきたかどうかを見て、master or slave のどちらを参照するか決める  1リクエスト内で Slave のデータを元に Master を更新すると、レプリ 遅延で古い Slave のデータを参照していてデータの不整合が起こる →更新系のリクエスト内で参照するDB は Master で統一する
  • 30. Copyright © DeNA Co.,Ltd. All Rights Reserved. KVSとの併用について  ゲームデータのキャッシュは難しい  更新を頻繁に行うのでキャッシュクリア処理が面倒  忘れると気づきづらい障害に  トランザクションとの整合性  Read Only のデータ(マスタ等)をキャッシュするのが一番楽
  • 31. Copyright © DeNA Co.,Ltd. All Rights Reserved.  ゲームサーバーのデータベースは整合性/負荷と の戦い  ノウハウを知って急激なアクセス増加にも耐えら れる構築/開発をしよう
  • 32. Copyright © DeNA Co.,Ltd. All Rights Reserved. おわり

Editor's Notes

  1. インデックス効かない場合の解決策 見出しは文字をでっかく