RDBに慣れた人にこそ知ってもらいたい、DynamoDBの特徴まとめ

こちらは [AWS for Games Advent Calendar 2022](https://qiita.com/advent-calendar/2022/aws-for-games) の5日目の記事です。

# はじめに

ゲーム開発にあたり、MySQLなどのRDBを使い慣れている方は多いと思いますが、DynamoDBをゲームで使うことを考えたことはありますでしょうか。

「DynamoDB…AWSのNoSQLのサービスですよね。なんかよく知らないけどRDBで困ってないし別にいいじゃん？」
「なんかいいこともあるって話は聞くけど、いろいろ勉強しなおさないといけないんでしょ…コスパ悪いっしょ」
そんな風に考えた方も、多いかもしれません。

この記事はそんなRDBユーザーの方にこそ知ってもらいたい、DynamoDBをゲームで使うにあたってのギモンをFAQ形式で見ていきたいと思います。テクニカルなことよりも、性質や雰囲気重視でラフな文章でまとめていきます。では早速いきましょう。

# DynamoDBに関するFAQ

## Q1. なんでDynamoDBを使いたくなるのですか？

まずはこれですね。なんで使いたくなるのでしょう。
結論から言うと **「スケーラビリティの高さ」と「管理負担の削減」** です。
その意味を理解するために、まずそもそもRDBの利点ってなんだったか、考えてみます。自然と使っちゃっているのであまり改めて考えたこともないかもしれないですね。私の解釈では以下の通りです。

- 汎用性の高いデータモデルを扱える
- SQLを用いた柔軟なクエリが書ける
- （その結果、）データモデル設計をアプリ（ビジネスロジック）実装と切り離して抽象化することで、変更耐性の向上や分業を実現できる

歴史的には、この抽象度の高さがシステム開発体制のスケールを実現した要因だったという認識を持っています。（もちろんそれだけではないと思いますが）
一方でRDBの課題の一つにスケーラビリティがあります。例えばMySQLで書き込み性能をスケールさせるにはシャーディング構成を採る必要が出てきます。一定規模のゲームではおなじみかと思いますが、シャードの適切な管理はそれなりに（相当に？）負担でしょう。

ではDynamoDBの特徴を見ていきます。

**スケーラビリティの高さ**
ゲームはヒットを願って開発しますが、結果を予測するのは当然難しいものです。DynamoDBを使うと、思ったよりヒットしても、残念ながらヒットしなかったとしても、データサイズに依らず一貫した性能（レイテンシー）を提供できるのがポイントです。裏側ではデータは自動でシャーディングされます。
事例として、Amazon Gamesが制作し2021年9月にリリースされたMMORPGである[NewWorldの紹介ブログ](https://aws.amazon.com/jp/blogs/news/the-unique-architecture-behind-amazon-games-seamless-mmo-new-world/)にはこうあります。
> Amazon DynamoDB￿を利用することで、開発時には1台だったテストサーバーを、コードを変更することなく500台以上の本番サーバーに拡張することができました。

RDBだとシャーディングの台数を増やしたり、改めて負荷試験してみたり、いろいろ検討事項がありそうな場面ですね。一貫した性能、とはそういうことです。

**管理負担の軽減**
プレイヤーの多い時間帯に合わせたスケーリング運用や、イベント時のスパイク負荷への対応、これらはゲームにおけるDB運用の典型例ですが、DynamoDBではこれらの対応が楽になります。DynamoDBの提供するパフォーマンスは数値で設定する「キャパシティユニット」の値で決まり、この値を適切に管理する「キャパシティ管理」がほぼ唯一の管理負担になります。「DBエンジンのバージョンアップ対応」のような作業も不要です。

この2つの利点を享受したいがためにDynamoDBを使ってみよう！となることが多い印象です。
え？そんなにスケーラビリティとかいらないし、使いどころなさそう？いえ、スケーラビリティが不要だったとしても、管理負担の軽減だけを目的に導入するのもあり寄りのありだと思います。全てのメリットを享受しないといけないわけではないですね。そのあたりはQ5で詳しくお話しします。

## Q2. RDBと比較して、DynamoDBが適していないことはなんですか？

メリットがわかったところで、デメリットも知らないと使いどころがわからないですね。**適さないユースケースを正しく把握して、適材適所で使えるようにする**。これはとても大切なことです。

DynamoDBは元来キーバリュー型のNoSQLデータベースですのでそれに起因する形になりますが、データ分析（OLAP）系のユースケースには不向きです。また項目（レコードにあたる概念）あたりの[サイズ上限が400KB](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/ServiceQuotas.html#limits-items)（2022/12時点）であり、大きなバイナリデータを直接格納する用途も向きません。S3にデータを保存しDynamoDBにはリンクを保存するなどで対処します。

また機能的制約について、RDBとの比較としては以下3点の考慮ポイントがあります。

1. **トランザクションの制約**
以前は大きかったのですが、アップデートが重ねられ、今はトランザクション処理もできますし、[2022/9/6のupdate](https://aws.amazon.com/jp/about-aws/whats-new/2022/09/amazon-dynamodb-supports-100-actions-per-transaction/)ではついに100件までトランザクション処理が可能とりました（アップデート以前は25件まで）。これよりトランザクション関連は対応できる要件の幅が広がっています。
1. **検索条件の柔軟性の制約**
グローバルセカンダリインデックス（GSI）のようなインデックスの仕組みにより、多くの検索パターンに対応が可能ではあるものの、SQLと比較すれば柔軟性には欠けます。例えば複数のAND/ORのような複雑な検索条件をDynamoDBで実行する際、Scan + FilterExpressionのような実装にしてしまうと、コスパが悪くなってしまいます。（[参考:スキャンのパフォーマンスに関する考慮事項](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/bp-query-scan.html#bp-query-scan-performance)）
ですがちょっと考えてみてください。ゲームでそれほど複雑な条件を必要とするクエリはどのくらいあるでしょうか。複雑な条件を必要とするクエリが一部機能だけであれば、マイクロサービスとして切り出すこともできるかもしれません…おっと少し話がそれました。要は**制約の影響を具体的に評価しよう**、ということです。
RDBのSQLがなぜ検索条件の高い柔軟性を持っているかというと、実行計画を動的に生成するから、というのが一つの理由かと思います。それゆえにパフォーマンスの一貫性を保つことが課題となったり、負荷試験によるクエリチューニングが必要となります。そこがDynamoDBとのトレードオフとなるポイントです。
もしDynamoDBでは適さないユースケースも同時に扱う場合、[RDBとのハイブリッド構成](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/bp-hybrid.html)も検討余地はあります。
1. **Joinの制約**
DynamoDBで複数のテーブルを扱うことももちろん可能ですが、その際テーブル間のJoinはできません。DynamoDBでは以下のような工夫をします。
・アプリケーション側で複数回のクエリを発行してデータを結合する。
・単一のテーブルに項目をまとめて、一回のクエリで目的のデータが取得できる設計とする。
これはいわゆるマルチテーブル vs シングルテーブルという構図になります。シングルテーブルとは、なるべく一つのテーブルにデータをまとめましょう、というDynamoDBのテーブル設計指針の一つです。どちらもメリデメはありますが、シングルテーブルを目指すことでDynamoDBの理解は深まり、コストパフォーマンスも上げられる傾向があります。是非ブログ「[Single-table vs. multi-table design in Amazon DynamoDB](https://aws.amazon.com/jp/blogs/database/single-table-vs-multi-table-design-in-amazon-dynamodb/)」も確認してみてください。

ちなみにDynamoDBのテーブル設計ノウハウを学んだことがある方は、**アクセスパターンから設計する**というのを聞いたことがあるかもしれません。この理由が上記2、3の制約に関係しています。アクセスパターンを考慮することで、2、3の制約をうまくハンドリングでき、コストパフォーマンスの最適化につながるのです。

## Q3. DynamoDBを使いこなすには独特のテクニックを多く学ぶ必要があるようで、なんかつらそうです。

Q2でいくつかテクニックに言及しました。アクセスパターンから設計する、シングルテーブル、GSI、などなど、いろいろなテクニックがあります。これらを学ぶ必要があることにためらいを感じるのは仕方ないことだと思います。ですが一つ意識いただきたいのは、RDBの設計・運用においても実際多くのテクニックを使っている、という事実です。例を挙げてみます。

- Read性能：リードレプリカを利用してスケールさせるが、レプリカラグがあるために結果整合性を許容している。低レイテンシが必要な箇所はRedisなどのインメモリデータベースを利用するが、キャッシュ戦略の実装などアプリケーション設計の工夫を許容している。
- Write性能：プライマリインスタンスのスケールアップの限界を許容する。またはシャーディングの場合、アプリケーションの複雑性や、シャードまたぎのトランザクションができないことを許容してアプリケーションを設計している。
- テーブル設計：正規化、INDEXの張り方など、RDBならではのテクニックを正しく使う必要性を許容している。
- スキーマ変更：メンテナンス枠を設けてALTER TABLEする運用を許容している。
- チューニング：実行計画の動作を熟知してクエリをチューニングする。負荷テストでのメトリクス確認を通じて、クエリを最適化する手間を許容している。

いかがでしょうか。おそらく「あー、言われればそうだなあ。許容している、って考えたことなかったわ」と思われるかと思います。DynamoDBを使うにあたっても、何を許容し何を得るか、がRDBとは異なるというだけです。むしろ特徴の違う複数のDBをうまく使い分けられるとよさそうだな、と思いませんか？

ちなみにDynamoDBは、「大変さを設計フェーズに寄せている」と考えることもできます。**設計フェーズで適切に作りこむほど、運用フェーズで楽できる**という感じです。設計が大変そうに感じても、その分運用が楽になる、という面は実際に使ってみないと実感わかなそうので、ここでお伝えしたかったのです。カプコンの事例「[インフラエンジニアは動かない](https://pages.awscloud.com/rs/112-TZM-766/images/CUS-60_AWS_Summit_Online_2020_CAPCOM.pdf)」も是非参考にしてください。

## Q4. 「アクセスパターンから設計する」と言いますが、作ったあと運用中にアクセスパターンが変わってしまったら、どうすればよいのですか？

DynamoDBの「アクセスパターンから設計する」を聞いたとき、一定数の方がこの疑問を持ったかと思います。
まずそもそも、設計変更が必要になるのはどんな時なのでしょうか。DynamoDBはPK(Partition Key)、SK(Sort Key)を設計時にしっかり決めますが、他の属性は基本自由です。なので**基本的にはPK/SKの選び方が変わらない限り柔軟に対応できるケースがほとんど**と思われます。
特にゲームの場合、多くのクエリのPKがユーザーIDであるため、他のジャンルと比較するとむしろDynamoDBで扱いやすいと言えます。
またクエリの要件追加であれば、GSIの追加だけで対応できる場合もあります。

とはいえどうしても変更が必要となることもあるかもしれません。その場合でも、例えば以下のような方法で変更できます。ちょっと文章だけだとわかりにくいですが、やりようはあるということと、特にDynamoDBに限った話でもないという点が伝われば十分かなと思います。むしろDynamoDBだとまだやりやすい、という面もあるでしょう。

1. Full export + Full import
データをエクスポートし、新しい形式に加工し、インポートする、というシンプルな方法です。メンテ時間を設ける必要がありますが、手順は一番わかりやすいでしょう。
DynamoDBはS3連携でデータのエクスポート・インポートができるため、かなりシンプルです。
2. Full export + Full imoprt + CDC (Change data capture)
1の方法に加え、DynamoDB Streamsを使って差分更新を非同期で行い、差分が最小となった時点で切り替える方法です。1の方法で必要だったメンテ時間を最小化できる点がメリットです。
DynamoDBではDynamoDB Streamsによりデータ更新をイベント処理する仕組みが作りやすいという特徴があります。
3. Double Read/Write
詳細は割愛しますが、新しいPK/SKを持ったテーブルを新規作成し、現行テーブルと両方に書き込み・読み込みをしつつ、徐々に新しい方のみに寄せていくようなイメージです。うまくやればノーメンテで移行できますが、DynamoDBなら1、2の方法のほうがわかりやすそうですね。

細かい話になりましたが、少なくとも初期設計時点でもう引き返せないようなものではないということです。特にゲームならPKはほぼユーザーIDでしょうから、身構えすぎなくてもいいのではないかと思います。

## Q5. DynamoDB使ってみたいですが、どこからどう進めればいいでしょうか…？

最初から頑張ると疲れてしまうので、シンプルかつ適したユースケースで、小さなところから始めてみるのがおすすめです。キーバリューなので、普段Redisを使っているセッションストアのようなものからDynamoDBを試してみるとか。そこでDynamoDBの運用含め慣れていくことで、ステップアップする形で使いどころを広げていけるはずです。
DynamoDBはデータモデリングの進め方も特徴的ですが、これも最初から完璧を目指さなくても、RDBライクなモデルでマルチテーブルで実装してみてもいいと思うのです。もちろんコストパフォーマンスは最適化されませんが、それだけでも「スケーラビリティ」「運用負担の削減」のメリットは得られてしまいます。そこから少しずつ、数々のテクニックを学びながらコストパフォーマンスを高めていくことができます。
何か具体的なプロジェクトの横で同じものをDynamoDBで実装してみるPoC（検証プロジェクト）などの建付けで、試行錯誤しながらやってみてコツをつかんでいく、というのもよいでしょう。どこまで最適化を頑張るかはやりながら決めればいいのです。

# DynamoDBやってみよう！

ここまで読んでいただきありがとうございました。きっと、DynamoDB使うとたどり着けそうな状態と、そこに至るまでの道筋が、なんとなーくぼんやりとイメージいただけたのではないかな、と期待しています。いまの状態で以下の参考資料に目を通していただくと、内容が腑に落ちやすくなっているはずですし、イメージがより具体的になると思います。興味あるものから是非確認してみてください！

## 参考資料

**全体**
[新年の抱負 : Amazon DynamoDB のベストプラクティスを守る](https://aws.amazon.com/jp/blogs/news/make-a-new-years-resolution-follow-amazon-dynamodb-best-practices/)
[DynamoDB を使用した設計とアーキテクチャの設計に関するベストプラクティス](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/best-practices.html)

**テーブル設計ノウハウ関連**
[DynamoDBの基礎と設計](https://speakerdeck.com/_kensh/dynamodb-design-practice)
[DynamoDB のNoSQL設計](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/bp-general-nosql-design.html)
[DynamoDB でリレーショナルデータをモデル化するためのベストプラクティス](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/bp-relational-modeling.html)
[Single-table vs. multi-table design in Amazon DynamoDB](https://aws.amazon.com/jp/blogs/database/single-table-vs-multi-table-design-in-amazon-dynamodb/)

**サンプルデータモデル・サンプルコード**
[NoSQL Workbench のサンプルデータモデル](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/workbench.SampleModels.html)
[Amazon DynamoDBで作るサーバーレスなゲーム用アチーブメントマイクロサービス](https://aws.amazon.com/jp/builders-flash/202205/serverless-game-achv-microservice-dynamodb/)
[Amazon DynamoDBで作るサーバーレスなゲーム用フレンドマイクロサービス](https://aws.amazon.com/jp/builders-flash/202209/dynamodb-serverless-friend-microservice/)



(免責) 本記事の内容はあくまでも個人の意見であり、所属する企業や団体は関係ございません。