amazon dynamodb テーブル設計と 実践 tips · pdf fileamazon dynamodbとは •...
TRANSCRIPT
© 2014 Amazon.com, Inc. and its affiliates. All rights reserved. May not be copied, modified, or distributed in whole or in part without the express consent of Amazon.com, Inc.
Amazon DynamoDB テーブル設計と実践 Tips
アマゾンデータサービスジャパン株式会社
シニアソリューションアーキテクト
安川 健太
Session #TA-10
自己紹介
• 安川 健太 (@thenkentiest)
– AWSソリューションアーキテクト
– 担当するお客様の範囲
• スタートアップ
• ゲーム・ソーシャルサービス
• 時々エンタープライズ
• 好きなAWSサービス – Amazon DynamoDB, Amazon Cognito
Amazon DynamoDBとは • NoSQL as a Service
• 超高速・予測可能な一貫したパフォーマンス
• シームレスなスケーラビリティと低コスト
運用管理必要なし
低レイテンシ、SSD
プロビジョンスループット
無限に使えるストレージ
ADMIN
Amazon DynamoDBの特徴 • 管理不要かつ高い可用性と堅牢性
• プロビジョンドスループット
• 容量制限のないストレージキャパシティ
クライアント
ご利用のお客様の一部
AWSの利用: 人気のドット絵ファンタジー 「騎士とドラゴン」のインフラをAWSで運用
ビジネス効果: 少数精鋭で開発を行う中で、運用面の負担を最小化、ゲームの開発リソースを最大化
短期間でのDAUの急上昇もわずかなスケールパラメータの調整で対応
DAUが5,000から30,000に6日間で跳ね上がった時の作業 AWS Elastic Beanstalkのインスタンス数 ↑ Amazon DynamoDBのスループット ↑
Amazon DynamoDBに増え続けるチャットデータを 確実に保存しつつ、サービスの成長に備える
AWSの利用: 2012年のサービスの開始時からEC2上にシステムを構築
ビジネス効果: Amazon DynamoDBの導入で増え続けるデータの心配や、バックアップやメンテナンスを考慮する必要がなくなった
課題: MySQLとRedisでは増え続ける思い出のデータを保持しながらのサービス成長が難しい
RedisからAmazon DynamoDBへ
EC2
Redis
タイムスタンプでの検索が主用途だったのでRangeキーを用いた検索で置き換え Redisは高機能だがデータ保
存容量の単価は高い
http://timers-tech.hatenablog.com/entry/2013/10/31/232027
Amazon DynamoDBの基礎知識
TableのKey, Indexとクエリ操作
Hash Key Hash Key + Range Key
Local Secondary Index
Global Secondary Index
Scan Scan
Query
(Batch)GetItem
(Batch)PutItem
UpdateItem
DeleteItem
+
Range Key以外でのQuery
Hash Keyを超えたQuery
+
+
Table操作についての基礎知識
• 1 アイテムの更新はアトミックに可
• 読み込みの一貫性は2種類 • Strongly Consistent
• Eventually Consistent
• Filterを設定したScanやQuery
並列アクセスの制御
• Conditional Update • Attribute Xの値が○○に等しい/以上/以下だったら」などの条件を付
けた更新
• Set型を使う際には値がSetに含まれるかどうかも条件に設定可
• Atomic Counter • 値の加算・減算などの操作を競合なく実行
• UpdateItemのADDを利用
テーブル設計&クエリ例
ユースケースごとのテーブル設計及びクエリの例
1. アプリのイベント履歴管理 – Hash + Range keyの利用例
2. ソーシャル画像共有アプリ – 複数テーブルによるデータモデル、LSI、GSIの利用例
3. マルチプレーヤゲーム – Conditional UpdateとAtomic Counter
4. 投票システム – Write Sharding
アプリのイベント履歴管理
User
(Hash)
Timestamp
(Range)
Opponent Result
Alice 2014-07-11 12:21:20 Bob Lost
Alice 2014-07-11 12:42:01 Bob Won
Alice 2014-07-14 09:48:00 Dan Won
Alice 2014-07-15 16:21:11 Charlie Won
Battle History
ユーザIDをHash Key + Time StampをRange Keyに
– ユーザごとにTimestampで範囲指定してクエリ可
Charlie
02-25 16:21
Won!
Your Battle History
Dan
02-24 09:48
Won!
Alice
02-21 12:42
Won!
Amazon DynamoDBのデータも解析に利用
• Amazon Elastic MapReduce (EMR)で読み出し
• Amazon Redshiftで直接読み込み
• EMRでFilterした後、Redshiftに読み込み
• etc
EC2
Time Based Partition Tables
Log-2014-July Log-2014-June Log-2013-June
Delete Table Simple
Query COPY
ソーシャル画像共有アプリ
Home My Posts My Profile
Bob
Steak! 10:18
Carol
BBQ! w/Alice 10:12
Dan
Riajuee… 10:11
Alice
Beer! 10:21
Alice
BBQ! w/Carol 10:12
Alice
Starting BBQ! 10:09
Name:
Alice
Mail: foo
Profile: some texts
テーブル設計
Users Table Friends Table
2つのテーブルを定義
• ユーザ情報テーブル
• 友達リストテーブル
User
(Hash)
Nicknames
Bob [ Rob, Bobby ]
Alice [ Allie ]
Carol [ Caroline ]
Dan [ Daniel, Danny ]
友達一覧を取得
Users Table
Item
Attribute (string, number, binary, set)
Primary Key (Hash)
User
(Hash)
Nicknames
Bob [ Rob, Bobby ]
Alice [ Allie ]
Carol [ Caroline ]
Dan [ Daniel, Danny ]
友達一覧を取得
Friends Table
User
(Hash)
Friend
(Range)
Bob Alice
Alice Bob
Alice Carol
Alice Dan
Users Table
Hash + Range Primary Key
Friends Table Users Table
User
(Hash)
Nicknames
Bob [ Rob, Bobby ]
Alice [ Allie ]
Carol [ Caroline ]
Dan [ Daniel, Danny ]
User
(Hash)
Friend
(Range)
Bob Alice
Alice Bob
Alice Carol
Alice Dan
友達一覧を取得
Aliceの友達一覧を取得
1. Query (Table = Friends, Hash = Alice, Range = *)
2. BatchGetItem(Bob, Carol, Dan)
投稿画像の保存と検索
Images Table
User
(Hash)
Image
(Range)
Date Link
Bob aed4c 2013-10-01 s3://…
Bob cf2e2 2013-09-05 s3://…
Bob f93bae 2013-10-08 s3://…
Alice ca61a 2013-09-12 s3://…
Bob
Bobの投稿画像一覧を取得 Query (Table=Images, Hash= Bob, Range=*)
でもある時刻以降の画像を取得したかったら…?
ある日時の画像取得
Images Table
User Image Date Link
Bob aed4c 2013-10-01 s3://…
Bob cf2e2 2013-09-05 s3://…
Bob f93bae 2013-10-08 s3://…
Alice ca61a 2013-09-12 s3://…
User Date Image
Bob 2013-09-05 cf2e2
Bob 2013-10-01 aed4c
Bob 2013-10-08 f93bae
Alice 2013-09-12 ca61a
Table ByDate Local Secondary Index
Local Secondary Index をDateに張る
画像にユーザのタグ付け
ImageTags Table
Image User
aed4c Alice
aed4c Bob
f93bae Alice
f93bae Bob
Image f93baeにAliceをタグ付け PutItem(Table = ImageTags, Hash = f93bae, Range = Alice)
Bob
でもあるユーザがタグ付けされてる画像の一覧を取得したかったら…?
Image f93baeにタグ付けされたユーザ一覧 Query(Table = ImageTags, Hash = f93bae, Range = *)
ユーザのタグ付き画像一覧
ImageTags Table
UserにImageをRangeキーとした Global Secondary Indexを張る
User
(Hash)
Image
(Range)
Bob aed4c
Bob f93bae
Alice aed4c
Alice f93bae
ByUser Global Secondary Index
Image
(Hash)
User
(Range)
aed4c Alice
aed4c Bob
f93bae Alice
f93bae Bob
Table
Bob
Aliceがタグ付けされた画像一覧
マルチプレーヤーバトル
Characters designed by Bleed, AnthonyMyers and surt
at http://opengameart.org and provided under CC license
RDBでやろうとすると…
• ボスのレコードをロック
• HPからダメージ分を引く
• ボスのレコードを更新
• ボスのレコードのロックを解除
低遅延での実行は難しい上、DBにも大きな負荷が
DynamoDBの場合
• HP > 0を条件にしたConditional Write – 成功したらダメージ反映完了
– 条件にあわずに失敗したら既に誰かが倒したってことで処理
通常の1アイテム更新と同じコスト&パフォーマンス!
戦士の攻撃
{
Id : fighter123,
HP : 250,
MP : 100,
Attack : 100,
Defence : 50,
LastHitBy: null
}
Character Status
{
Id : fighter123,
HP : 4890,
MP : 800,
Attack : 250,
Defence : 150,
LastHitBy : null
}
ドラゴンに80のダメージを与えた
{
Id : fighter123,
HP : 250,
MP : 100,
Attack : 100,
Defence : 50,
LastHitBy:
}
Character Status
{
Id : dragon234,
HP : 4810,
MP : 800,
Attack : 250,
Defence : 150,
LastHitBy : figher123
}
Update:
HP : -80,
LastHitBy: figher123
Expect:
HP > 0
魔法使いはいかづちの杖を振りかざした
{
Id : wizard456,
HP : 980,
MP : 400,
Attack : 20,
Defence : 40,
LastHitBy: null
}
Character Status
{
Id : dragon234,
HP : 4810,
MP : 800,
Attack : 250,
Defence : 150,
LastHitBy : figher123
}
杖からいかづちがほとばしる
{
Id : wizard456,
HP : 980,
MP : 400,
Attack : 20,
Defence : 40,
LastHitBy: null
}
Character Status
{
Id : dragon234,
HP : -5189,
MP : 800,
Attack : 250,
Defence : 150,
LastHitBy : wizard456
}
Update:
HP : -9999,
LastHitBy: wizard456
Expect:
HP > 0
再び戦士の攻撃
Character Status
{
Id : dragon234,
HP : -5189,
MP : 800,
Attack : 250,
Defence : 150,
LastHitBy : wizard456
}
Update:
HP : -78,
LastHitBy: figher123
Expect:
HP > 0
{
Id : fighter123,
HP : 250,
MP : 100,
Attack : 100,
Defence : 50,
LastHitBy:
}
ConditionalCheckFailedException
投票システム
投票者
Votes Table
候補者 A
得票数: 20
候補者 B
得票数: 30
投票システム
Votes Table
候補者 A
得票数: 21
候補者 B
得票数: 30
UpdateItem
ADD 1 to “候補者 A”
投票者
投票に備えてDynamoDBをスケール
Votes Table
200,000 Write Capacity Unitsをプロビジョニング
Partition 1
600
writes/s
投票者
Partition L
600
writes/s
Partition M
600
writes/s
Partition N
600
writes/s
Votes Table
パーティションへのアクセスに偏り
候補者A 候補者B
投票者
Partition 1
600
writes/s
Partition L
600
writes/s
Partition M
600
writes/s
Partition N
600
writes/s
Write ShardingでPartition負荷を分散
• 候補者IDにランダムなSuffixをつけて書き込み
• 集計時は全Suffixにクエリ
候補者ID 得票数
候補者A_0 1,848
候補者A_1 2,183
候補者B_0 4,084
候補者B_1 3,984
“候補者A_” + rand(0, 10)
投票者
候補者Aに投票 +1
+1
Write Shardingした上での投票
Votes Table
候補者 A_2
候補者 B_1
候補者 B_2
候補者 B_3
候補者 B_5
候補者 B_4
候補者 B_7
候補者 B_6
候補者 A_1
候補者 A_3
候補者 A_4 候補者 A_7 候補者 B_8
投票者
UpdateItem: “候補者A_” + rand(0, 10)
ADD 1 to 得票数
候補者 A_6 候補者 A_8
候補者 A_5
Write Shardingした場合の集計
Votes Table
候補者 A_2
候補者 B_1
候補者 B_2
候補者 B_3
候補者 B_5
候補者 B_4
候補者 B_7
候補者 B_6
候補者 A_1
候補者 A_3
候補者 A_4
候補者 A_5
候補者 A_6 候補者 A_8
候補者 A_7 候補者 B_8
バッチ
プロセス
候補者 A
Total: 2.5M
1. 合算
2. 保存 投票者
まとめ
• ユーザの行動履歴 – Hash+Range Key
– Time Based Partition Table
• ソーシャル画像共有アプリ – Index Table
– LSI / GSI
• マルチプレーヤーバトル – Conditional Update + Atomic Counter
• 投票システム – Write Sharding
2014.09.09 SAVE THE DATE
http://csd.awseventsjapan.com/
検 索 Cloud Storage & DB Day