Top Banner
Copyright © DeNA Co.,Ltd. All Rights Reserved. ゲゲゲゲゲゲゲゲゲゲゲゲ ゲゲゲゲゲゲゲゲ ゲゲゲゲ DeNA Games Osaka ゲゲゲゲゲ ゲゲ 西 [email protected]
32

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

Apr 21, 2017

Download

Engineering

sairoutine
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: ゲームエンジニアのためのデータベース設計

Copyright © DeNA Co.,Ltd. All Rights Reserved.

ゲームエンジニアのための

データベース設計

株式会社 DeNA Games Osaka技術編成部

人西 聖樹 [email protected]

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

自己紹介

人西聖樹 ( ひとにし まさき )

株式会社 DeNA Games Osaka 2014 年 入社

Web アプリケーションエンジニア

シューティングゲーム好き。東方 Project 大好き

某 400 万人ユーザー超えモバイルゲームの 開発やってます

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

ゲームのサーバーサイド

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

データをどうやって保存しようか?

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

多様な選択肢

RDBMS   MySQL   Oracle   PostgreSQL KVS   Redis   Riak カラム指向型

   Apache Cassandra   Hbase ドキュメント指向

   MongoDB   Apache CouchDB   etc…

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

今日は MySQL の話をします!

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

突然ですがアンケート

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

MySQL 使った事ある人!

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

explain コマンド使ったことある人!

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

InnoDB におけるギャップロック とネクストキーロックについて説明できる人!

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

本日のテーマ

RDBMS ( リレーショナルデータベース ) とは

データベース観点でのゲームのデータの特徴

データベース構築時に気をつけること

アプリケーション開発時に気をつけること

Page 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 さんがどのアイテムを何個所持しているか取得することができる。

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

ACID 特性

Atomicity 原子性

  トランザクションの操作は全て実行されるか

  まったく実行されないかのどちらか

Consistency 一貫性

  トランザクション開始時と終了時にデータの

  整合性が保たれる

Isolation 独立性

  他のトランザクションによる操作の影響を受けない

Durability 永続性

  コミットしたトランザクションのデータは保存される

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

ゲームデータの特徴

ユーザーを primary key としたレコードが多い

永続データと期間限定データ ( イベントのデータ等 ) がある

マスタデータ (read only) が多い

  アイテムマスタ、ボスマスタ、ボス出現マスタ etc… レコードの状態更新が多い

  ボスの HP 減少、 HP 回復、マップ移動 etc… 可用性・整合性は大切

  ゲームがプレイできない、アイテムを使用したのに

  回復していない等の不具合・障害に対して、

  課金しているユーザーの温度感は非常に高い

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

データベース構築時に考えること

Master/Slave 構成 垂直分割 水平分割

  垂直/水平分割はアプリケーション側で対応しないといけない

   (MySQL 側に仕組みがない ) が後から追加するのは

  大変なので最初から考慮して開発する

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

Master / Slave 構成

Master

Slave Slave Slave

レプリケーション

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

ゲームは更新系クエリがめちゃ多い

ボタンを押すだけでステータス更新

体力増減とか

ボスへダメージとか

アイテム獲得とか

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

参照系クエリは Slave に逃せる。

Slave のスケールアウトは容易

更新系クエリは必ず Master に I/O負荷がかかる → Master はボトルネックになりがち

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

垂直分割

A テーブルB テーブルC テーブル

D テーブルE テーブルF テーブル

G テーブルH テーブルI テーブル

テーブルの種類によって DB を分割。 Join 句が使えなくなる。 テーブルへのアクセス数が均等になるように分割しないと負荷が偏る

Page 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 のレコードはこっち

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

複数のトランザクションを扱う

複数 DB へのトランザクションをどう扱うか?

InnoDB の REPEATABLE READ は最初のクエリ発行時に取得できるレコードの値が決定するので、各 DB に対して最初のクエリをいつ投げるか意識する必要がある。

コミットタイミングは全て同一で行うのが楽

コミットタイミングが別々だとデータ不整合が起こりやすくなる (片方はコミット済みなのにもう片方はロールバックとか…

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

アプリケーション開発時に気をつけること

適切な index と index を使える適切なクエリ

クエリ発行量を減らす

行ロック

レプリ遅延対策

Repeatable read の特性

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

インデックス

InnoDB のインデックスは B+ Tree

常に一定の深度になるようにバランス化された木構造1 レコードの取得に対して O(log N) で探索することができる

Page 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 |+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

オプティマイザMySQL はコストベース・オプティマイザ コストベースのオプティマイザでは、統計情報だけなく      CPU クロック     メモリ容量      DISK I/O速度      DBMS でのパラメータ で実行計画が決定される

 開発環境と同じ実行計画になるとは限らない     データ件数が違う     データの種類が違う      CPU クロックが違う     メモリ容量が違う               など

MySQL のオプティマイザは実行計画を結構見誤る不安ならば FORCE INDEX で使用するインデックスを指定する

Page 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 はインデックスを使えない)

Page 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 で実行できる。

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

行ロック

同時操作を常に意識する

   A さんと B さんが同時にボスを攻撃したら両方ともボスを撃破した扱いになったり…

   A さんが 2端末使って、同時にアイテムを受け取りを押すことでアイテム増殖できたり…

前者は攻撃時にまずボスレコードをロックして、 A さんと B さんの処理を直列させることで防げる

後者はアイテム受け取り時に A さんのレコードをロックして処理を直列させることで防げる

ロックの順番を統一しないと、デッドロックが発生する。 必ず存在するレコードに対してロックを取る 存在しないレコードをロックすると、 InnoDB の Repeatable Read

では gap lock が発生し、広範囲にロックを獲得する。

  → lock wait timeout

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

レプリケーション遅延対策

大量クエリのコミット等でレプリ遅延 (master DB への変更が slave DB へ反映が遅れること ) が発生する

回復アイテムの使用 (Master を更新 )→次ページで使用結果を見ると回復していない (Slave にまだ回復の反映が遅れてる )

  →更新処理後、更新したデータを cache に詰めて、遷移先で使用する

  →更新処理後、更新処理から遷移されてきたかどうかを見て、 master or slave のどちらを参照するか決める

1 リクエスト内で Slave のデータを元に Master を更新すると、レプリ遅延で古い Slave のデータを参照していてデータの不整合が起こる

  →更新系のリクエスト内で参照する DB は Master で統一する

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

KVS との併用について

ゲームデータのキャッシュは難しい

更新を頻繁に行うのでキャッシュクリア処理が面倒

忘れると気づきづらい障害に

トランザクションとの整合性

Read Only のデータ ( マスタ等 ) をキャッシュするのが一番楽

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

ゲームサーバーのデータベースは整合性/負荷との戦い

ノウハウを知って急激なアクセス増加にも耐えられる構築/開発をしよう

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

Copyright © DeNA Co.,Ltd. All Rights Reserved.

おわり