dynamodbによるソーシャルゲーム実装 how to

69
2013-03-16 JAWS DAYS 2013 [DEV- 02] 株株株株株株株株株 株株 株株 DynamoDB 株株株 ソソソソソソソソソソ How To

Upload: -

Post on 14-Jun-2015

15.361 views

Category:

Documents


4 download

DESCRIPTION

JAWS DAYS 2013 [DEV-02] http://jaws-ug.jp/jawsdays2013/

TRANSCRIPT

Page 1: DynamoDBによるソーシャルゲーム実装 How To

2013-03-16 JAWS DAYS 2013 [DEV-02]

株式会社マイネット 伊藤 祐策

DynamoDB によるソーシャルゲーム実装 How

To

Page 2: DynamoDBによるソーシャルゲーム実装 How To

概要

 『 DynamoDB× ソシャゲ』をテーマに

設計と実装の How To を惜しむことなく伝授しま

す!

Page 3: DynamoDBによるソーシャルゲーム実装 How To

本資料について

以下の URL からいつでもダウンロード可能です

http://iy-h.com/01/

読み上げ原稿も置いてあるので聞き逃したところがあっても安心!

Page 4: DynamoDBによるソーシャルゲーム実装 How To

DynamoDB の紹介

Page 5: DynamoDBによるソーシャルゲーム実装 How To

DynamoDB とは?

Key Value Store 型データベース

NoSQL REST API でアクセス

性能課金 読込性能、書込性能の予約量に応じて時間課

Page 6: DynamoDBによるソーシャルゲーム実装 How To

DynamoDB のここがスゴイ

ウルトラスケーラビリティ KVS 型データベースの最大の強み 課金すればするほど強くなる!

超絶耐障害性 書込み完了と同時に 70km 以上離れた 3

箇所のデータセンターに分散保存される!

Page 7: DynamoDBによるソーシャルゲーム実装 How To

予習

Page 8: DynamoDBによるソーシャルゲーム実装 How To

基本的な使い方

キーを指定してデータへアクセス CRUD 操作は一通り揃っている 操作は全てアトミックに処理される

キーと更新条件を指定してデータを更新する データの内容が更新条件に適合しなければ失敗する 楽観的ロックの実装に必要

Page 9: DynamoDBによるソーシャルゲーム実装 How To

他の KVS 型 DB との大きな違い

「レンジキー」というものがある。 テーブルのキーは以下の2通りから選択できる

ハッシュキーのみ ハッシュキー+レンジキーの組み合わせ

Page 10: DynamoDBによるソーシャルゲーム実装 How To

ハッシュキーとは?

データへアクセスするためのプライマリキー ハッシュキーを上手に分散させることができれ

ば概ね課金額通りの性能が得られる。

逆を言えば、 1 つのハッシュキーにアクセスが集中する設計にしてしまうと、パフォーマンスが低下してしまう。【重要】

Page 11: DynamoDBによるソーシャルゲーム実装 How To

レンジキーとは?

ハッシュキー+レンジキーでプライマリキーになる

Query メソッドで範囲検索が可能になる 「ハッシュキー」と「レンジキーの範囲」を指定

して複数レコードをまとめて取得することができる

レンジキーに対するアクセスを分散しても負荷が分散されないことに注意 ハッシュキーでしっかり分散される設計にしよう

Page 12: DynamoDBによるソーシャルゲーム実装 How To

基本的なレコード操作

CRUD PutItem ... 作成 / 置換 UpdateItem ... 全部更新 / 部分更新 DeleteItem ... 削除 GetItem ... 取得

複数レコードの取得 Query ... 1 つのハッシュキーに対する範囲検索 Scan ... テーブル内の全レコード取得

Page 13: DynamoDBによるソーシャルゲーム実装 How To

格納できる値

値の型は3種類から選べる String 型

UTF-8 文字列 Number 型

整数または小数 Binary 型

用途としては、暗号化されたデータ等

Page 14: DynamoDBによるソーシャルゲーム実装 How To

格納できない値

NULL 値 代わりに属性ごと削除する

長さ 0 の文字列 NULL と同様、属性ごと削除する

真偽値 Number 型の 1 と 0 で代用する

Page 15: DynamoDBによるソーシャルゲーム実装 How To

条件付きアップデート

更新系のメソッドで利用できる 条件に適合しなければ操作は失敗する 以下のような条件が指定可能

「もしレコードが存在しなければ」 「もしレコードが存在したならば」 「もしこの属性の値がこの内容と同一であれば」 「もしこの属性が存在しなければ」

Page 16: DynamoDBによるソーシャルゲーム実装 How To

DynamoDB の使いどころ

Page 17: DynamoDBによるソーシャルゲーム実装 How To

MySQL の代替手段となり得るか?

完全には無理。 以前 DynamoDB だけでソーシャルゲーム

作ってみましたが、結論としては色々と無理があるので MySQL 等とのハイブリット型にするのが良いです。

Page 18: DynamoDBによるソーシャルゲーム実装 How To

DynamoDB が苦手なこと

縦横無尽な検索 プライマリキー以外のインデックスを張れない せいぜいレンジキーで範囲検索ができるくらい

小規模な集計処理 集計機能がそもそもないので自前で実装する必要がある 大規模データなら EMR連携という手段が用意されてい

る 大きなデータの保存

1 レコードあたり 64kB というデータ長制限がある 素直に S3 使いましょう

Page 19: DynamoDBによるソーシャルゲーム実装 How To

MySQL との比較

DynamoDB MySQL

データ保全 ◎ ○検索 × ◎負荷分散 ◎ △

Page 20: DynamoDBによるソーシャルゲーム実装 How To

どう使うべきか?

アプリケーションに要求される機能のうち、 DynamoDB が苦手なものは他の手段に任せる 検索は MySQLや CloudSearch へ 集計は MySQL へ 大きなデータの保存は S3 へ

残ったものは全て DynamoDB で実装する

Page 21: DynamoDBによるソーシャルゲーム実装 How To

MySQL ハイブリッド型にする場合

MySQL のシステムがある日突然消失しても、すぐにサービスが再開できるような設計にしておく MySQL 内のデータは全て DynamoDB のデータを本体としたコピーにする。

データが全て消失しても、 MySQL インスタンスを作りなおして全データを再投入すれば完全復旧できるようにする。

「多少ロストしても構わないデータ」の分別をしっかりつけておく

Page 22: DynamoDBによるソーシャルゲーム実装 How To

弊社事例

Page 23: DynamoDBによるソーシャルゲーム実装 How To

大激闘!キズナバトル

Android アプリ 2012年 12月 26日リリース GvGカードバトル型ゲーム 最大 20人のチームを組んで、

1日 3回開催されるバトルを勝ち抜き、最強チームを目指す。

Page 24: DynamoDBによるソーシャルゲーム実装 How To

大激闘!キズナバトル

使用 DynamoDB テーブル数は 47 。 MySQL とのハイブリット型構成。 バトル開催時間になるとアクセス量は一気に 15倍に

なる。

12時19時

22時1日のアクセス数グラフ

Page 25: DynamoDBによるソーシャルゲーム実装 How To

DynamoDB の使われ方

原則全てのデータは DynamoDB で管理 ユーザー情報 ユーザーの所有物(カード、アイテム、 etc) チーム情報 バトル結果

MySQL で行なっている処理 対戦相手のマッチング ランキング集計 チーム検索 入団希望者検索

Page 26: DynamoDBによるソーシャルゲーム実装 How To

実装の基本方針 (1)

ユーザーが 1回行動するたびに 1 レコード作る アイテムを使った、カードバトルで対戦した、 etc ユーザーの行動が全て「証拠」として残されている。 お問い合わせからクレームが来た時に、何が起こったのかが明確

に分かるので調査が容易になる。 レコードは消さない

保存費用より Write 性能費用のほうが高い。 【速報】 3月 1日にデータ保存料金が 75% も値下げされまし

た!! リリース時から全ての歴史が保存されている。 KVS なのでデータ量がいくら増えてもパフォーマンスに影響が

ない。

Page 27: DynamoDBによるソーシャルゲーム実装 How To

実装の基本方針 (2)

ほぼ全ての処理をキューで非同期に実行 処理が終わるまでのタイムラグは画面エフェクトを表示して待

たせる いかに「ごまかす」かが腕の見せ所

キャッシュは TAT改善のために使う さすがに Memcached のほうが応答が速い。 Read 性能はかなり安いので節約する意味があまりない。 m1.small インスタンス 1台の費用で Read 性能を 366 も買え

てしまう。

Page 28: DynamoDBによるソーシャルゲーム実装 How To

テーブル設計

Page 29: DynamoDBによるソーシャルゲーム実装 How To

テーブル設計

スキーマレスだけどスキーマは定義する まずはゲームオブジェクトをクラスとして定義

ユーザー、所有カード、所有アイテム、 etc 1クラス=1スキーマ=1テーブル

class App_Record_Card extends DynamoDBRecord

ハッシュキーはユーザー ID で レンジキーはオブジェクトインスタンス ID で

インスタンス ID は日付+時刻+乱数で生成

Page 30: DynamoDBによるソーシャルゲーム実装 How To

テーブル定義の例

所有アイテム

ユーザー ID 100

所持金 1500G

薬草 32個

カード

ユーザー ID 100

インスタンスID

1001

レベル 10

ユーザー

ユーザー ID 100

名前 † ラインハルト†

レベル 15

Page 31: DynamoDBによるソーシャルゲーム実装 How To

実践

Page 32: DynamoDBによるソーシャルゲーム実装 How To

残念なお知らせ

全てのレコード操作メソッドは失敗する可能性がある。 TCP/IP ネットワークエラーが発生した場合 ( 結構頻繁 ) Endpoint側に障害が発生した場合 ( 数回実績あり ) 課金額以上の負荷を与えた場合 (何度もやらかした )

RDBMS における「トランザクション」は提供されていない 複数レコードを一貫性を保ったまま同時に更新することがで

きない。 アプリケーションレイヤで一貫性を保証する実装をしなけれ

ばならない。

Page 33: DynamoDBによるソーシャルゲーム実装 How To

更新対象が1レコードの場合

所有アイテム

ユーザーID

100

所持金 1500G

薬草 10個

Case 1:薬草を 1 個購入する

Page 34: DynamoDBによるソーシャルゲーム実装 How To

更新対象が1レコードの場合

所有アイテム

ユーザーID

100

所持金 1500G

薬草 10個

クエリ内容

ユーザーID

100

所持金 -100G

薬草 +1個

UpdateItem

【更新条件】所持金が 1500G だったら

Page 35: DynamoDBによるソーシャルゲーム実装 How To

更新対象が1レコードの場合

所有アイテム

ユーザーID

100

所持金 1400G

薬草 11個

更新完了!

Page 36: DynamoDBによるソーシャルゲーム実装 How To

更新対象が2レコード以上の場合

所有アイテム

ユーザーID

100

所持金 1500G

Case 2:カードを強化する

強化対象カード

ユーザー ID 100

インスタンスID

1001

レベル 10素材カード

ユーザー ID 100

インスタンスID

1002

レベル 1

Page 37: DynamoDBによるソーシャルゲーム実装 How To

更新対象が2レコード以上の場合

所有アイテム

ユーザーID

100

所持金 1500G

強化対象カード

ユーザー ID 100

インスタンスID

1001

レベル 10素材カード

ユーザー ID 100

インスタンスID

1002

レベル 1

更新削除

素材カードを消費して強化対象カードのレベルを1上げる。費用として 500G 徴収する。

Page 38: DynamoDBによるソーシャルゲーム実装 How To

更新対象が2レコード以上の場合

所有アイテム

ユーザーID

100

所持金 1000G

強化対象カード

ユーザー ID 100

インスタンスID

1001

レベル 10素材カード

ユーザー ID 100

インスタンスID

1002

レベル 1

Step1:所持金 -500G

Page 39: DynamoDBによるソーシャルゲーム実装 How To

更新対象が2レコード以上の場合

所有アイテム

ユーザーID

100

所持金 1000G

強化対象カード

ユーザー ID 100

インスタンスID

1001

レベル 10素材カード

ユーザー ID 100

インスタンスID

1002

レベル 1

Step2:素材カードを削除 削除

Page 40: DynamoDBによるソーシャルゲーム実装 How To

更新対象が2レコード以上の場合

所有アイテム

ユーザーID

100

所持金 1000G

強化対象カード

ユーザー ID 100

インスタンスID

1001

レベル 10素材カード

ユーザー ID 100

インスタンスID

1002

レベル 1

Step3:レベルアッ・・・

削除済

Page 41: DynamoDBによるソーシャルゲーム実装 How To

突然の死

Page 42: DynamoDBによるソーシャルゲーム実装 How To

更新対象が2レコード以上の場合

所有アイテム

ユーザーID

100

所持金 1000G

強化対象カード

ユーザー ID 100

インスタンスID

1001

レベル 10素材カード

ユーザー ID 100

インスタンスID

1002

レベル 1

残念!!カードの強化処理はこれで終わってしまった!

削除済

Page 43: DynamoDBによるソーシャルゲーム実装 How To

お問い合わせ内容

【ユーザー ID 】 100【ユーザー名】 †ラインハルト†【日時】 2013 年 3 月 16 日 16:25:58【お問い合わせ内容】お金とカードだけ取られた!!!ふざけんな補償しろ!!!

Page 44: DynamoDBによるソーシャルゲーム実装 How To

正しい実装パターン

Page 45: DynamoDBによるソーシャルゲーム実装 How To

用意するもの

Webサーバー Batchサーバー DynamoDBAmazon SQS

Page 46: DynamoDBによるソーシャルゲーム実装 How To

システム構成

DynamoDB

Web Servers

SQS

Batch Servers

1.HTTP Request

2.Put Record

3.Enqueue

4.Dequeue

5.Update Records

Page 47: DynamoDBによるソーシャルゲーム実装 How To

2種類のプロセス

Web リクエスト処理 HTTP リクエストをトリガーとして実行される処理 プロセスは Apache によって実行・管理される 途中でエラーが発生したら 503エラーを返して中断さ

れる キュー処理

SQS へメッセージを送り、メッセージの取り出しをトリガーとして実行される処理。

プロセスはアプリケーション用のユーザーで実行される

正常終了するまで何度も繰り返し実行される

Page 48: DynamoDBによるソーシャルゲーム実装 How To

Amazon SQS を使う

SQS は、処理の「完遂保証」のために使う 失敗した時は何度でも再実行されることを保証さ

せる キュー処理は最終的に正常終了に収束するよう

実装する 状態遷移図を書いてしっかり机上デバッグ

但し書いたら負けかなと思ってる 図が要らないほどシンプルな実装にしよう

Page 49: DynamoDBによるソーシャルゲーム実装 How To

キュー処理実装の鉄則

再実行耐性を持たせる 同じ処理が2回実行されても

結果に影響がでないようにする。並列実行耐性を持たせる

同じ処理が2つ以上のプロセスで並行して実行されても結果に影響がでないようにする。

Page 50: DynamoDBによるソーシャルゲーム実装 How To

再実行耐性の実装方法

入力内容から処理内容が全て決定されるようにする。 レコードの更新をする際に、確かに更新されたことが判別できるよう「証拠」を残すようにする。 更新日時を書き込む、ステータス値を変更す

る、 etc 。 レコード内容をみればどこまで処理が終わったかが

分かるようにする。 処理済みであればスキップして次の処理へ進むように

する。 複雑な分岐をさせず、上から流れ落ちるような処理に

する。

Page 51: DynamoDBによるソーシャルゲーム実装 How To

並列行耐性の実装方法

条件付きアップデート機能を用いて楽観的ロックを実装する。

更新する前にレコードを「一貫性あり」で読み込む。 レコードを更新するときは、「読み込んだ時点から他

の誰にも更新されていなければ」という条件をつける。 「条件付きアップデート」を使う 必要であればレコードにバージョン番号を導入する

更新に失敗した場合は処理を最初からやりなおす。 → 再実行耐性が実現されていれば問題ないはず!

Page 52: DynamoDBによるソーシャルゲーム実装 How To

処理単位のフローチャート

GetItem()

処理済み?UpdateItem() with Condition

throw RetryException次の処理へ

更新成功?

NO

NO

YESYES

Page 53: DynamoDBによるソーシャルゲーム実装 How To

キュー処理全体の流れ

処理単位1

開始

終了

処理単位2

処理単位3

流れ落ちるように

Page 54: DynamoDBによるソーシャルゲーム実装 How To

実践・改

Page 55: DynamoDBによるソーシャルゲーム実装 How To

カードの強化

所有アイテム

ユーザーID

100

所持金 1500G

Case 2':今度こそカードを強化する

強化対象カード

ユーザー ID 100

インスタンスID

1001

レベル 10素材カード

ユーザー ID 100

インスタンスID

1002

レベル 1

Page 56: DynamoDBによるソーシャルゲーム実装 How To

カードの強化

所有アイテム

ユーザーID

100

所持金 1500G

カード強化依頼

ユーザー ID 100

依頼 ID 5001

強化対象カード

1001

素材対象カード

1002

強化費用 500G

開始済 NO

Step1 :依頼レコードを作成する

Page 57: DynamoDBによるソーシャルゲーム実装 How To

カードの強化

Step2 :依頼 ID を所有アイテムレコードに登録する※STRING_SET 型を使う

所有アイテム

ユーザーID

100

所持金 1500G

未決済 [ 5001 ]

カード強化依頼

ユーザー ID 100

依頼 ID 5001

強化対象カード

1001

素材対象カード

1002

強化費用 500G

開始済 NO

Page 58: DynamoDBによるソーシャルゲーム実装 How To

カードの強化

Step3 :依頼 ID を強化対象カードレコードにも登録する※STRING_SET 型を使う

カード強化依頼

ユーザー ID 100

依頼 ID 5001

強化対象カード

1001

素材対象カード

1002

強化費用 500G

開始済 NO

強化対象カード

ユーザー ID 100

インスタンスID

1001

レベル 10

未処理 [ 5001 ]

Page 59: DynamoDBによるソーシャルゲーム実装 How To

カードの強化

キューメッセージ

処理種別 カード強化

ユーザーID

100

依頼 ID 5001Step4 :キューメッセージを発行する

カード強化依頼

ユーザー ID 100

依頼 ID 5001

強化対象カード

1001

素材対象カード

1002

強化費用 500G

開始済 NO

Page 60: DynamoDBによるソーシャルゲーム実装 How To

カードの強化 ( キュー処理 )

Step5 :レコードを読み込む

所有アイテム

ユーザーID

100

所持金 1500G

未決済 [ 5001 ]

カード強化依頼

ユーザー ID 100

依頼 ID 5001

強化対象カード

1001

素材対象カード

1002

強化費用 500G

開始済 NO

Page 61: DynamoDBによるソーシャルゲーム実装 How To

カードの強化 ( キュー処理 )

Step6 :開始済みにする※ 無条件 UPDATE

所有アイテム

ユーザーID

100

所持金 1500G

未決済 [ 5001 ]

カード強化依頼

ユーザー ID 100

依頼 ID 5001

強化対象カード

1001

素材対象カード

1002

強化費用 500G

開始済 YES

Page 62: DynamoDBによるソーシャルゲーム実装 How To

カードの強化 ( キュー処理 )

Step7 :決済する※ 条件付き UPDATE を使う

所有アイテム

ユーザーID

100

所持金 1000G

未決済 (NULL)

カード強化依頼

ユーザー ID 100

依頼 ID 5001

強化対象カード

1001

素材対象カード

1002

強化費用 500G

開始済 YES

Page 63: DynamoDBによるソーシャルゲーム実装 How To

カードの強化 ( キュー処理 )

Step8 :素材カードを削除

カード強化依頼

ユーザー ID 100

依頼 ID 5001

強化対象カード

1001

素材対象カード

1002

強化費用 500G

開始済 YES

素材カード

ユーザー ID 100

インスタンスID

1002

レベル 1

Page 64: DynamoDBによるソーシャルゲーム実装 How To

カードの強化 ( キュー処理 )

Step8 :素材カードを削除

カード強化依頼

ユーザー ID 100

依頼 ID 5001

強化対象カード

1001

素材対象カード

1002

強化費用 500G

開始済 YES

素材カード

ユーザー ID 100

インスタンスID

1002

レベル 1

削除

Page 65: DynamoDBによるソーシャルゲーム実装 How To

カードの強化 ( キュー処理 )

Step9 :強化対象カードのパラメータを加算する※ 条件付き UPDATE を使う

カード強化依頼

ユーザー ID 100

依頼 ID 5001

強化対象カード

1001

素材対象カード

1002

強化費用 500G

開始済 YES

強化対象カード

ユーザー ID 100

インスタンスID

1001

レベル 10

未処理 [ 5001 ]

Page 66: DynamoDBによるソーシャルゲーム実装 How To

カードの強化 ( キュー処理 )

Step9 :強化対象カードのパラメータを加算する※ 条件付き UPDATE を使う

カード強化依頼

ユーザー ID 100

依頼 ID 5001

強化対象カード

1001

素材対象カード

1002

強化費用 500G

開始済 YES

強化対象カード

ユーザー ID 100

インスタンスID

1001

レベル 11

未処理 ( NULL )

Page 67: DynamoDBによるソーシャルゲーム実装 How To

実装の要点

完遂保証のない処理 (Web リクエスト処理 )と、完遂保証のある処理 ( キュー処理 ) で、実行すべき処理を上手に振り分ける。

Web リクエスト処理の途中でエラーが発生しても、キュー処理の実行が開始されない限り「何も起こらなかった」ことになる。 その時は仕方なく 503エラーを返す 各レコードにトランザクション ID が残る可能性について

は、タイムアウト処理を別途実装することで対処する。

Page 68: DynamoDBによるソーシャルゲーム実装 How To

Page 69: DynamoDBによるソーシャルゲーム実装 How To

本資料について(再掲)

以下の URL からいつでもダウンロード可能です

http://iy-h.com/01/