maybe daily dev notes

私の開発日誌

DynamoDB、シングルテーブルにするか否か

はじめに

DynamoDBを使っていると、とかくテーブルは1つにまとめるべきという声や、複数テーブルからシングルテーブル設計に移行したという事例を耳にすることがあります。 しかし、その理由を聞いてみると、性能のためだったり管理を簡単にするためだったり、人により異なる印象です。 NoSQLにおける非正規化してデータをもつプラクティスは理解しつつ、その域を超えて全く無関係なItemを1テーブルにまとめる場合もあるようです。

私自身このトピックについて混乱していた中で、先日 The What, Why, and When of Single-Table Design with DynamoDB というブログを見つけました。 それを読んだ上で考えると、割と理解が整理できたので、この記事にまとめてみます。

なお私はDynamoDBの運用経験がまだ十分にあるわけではないので、勘違いや考慮漏れなどあるかもしれません。ツッコミお待ちしております。

シングルテーブル設計とは

あるアプリケーションが使うDynamoDBのテーブル数を1つに集約する設計方針です。 dynamodb single table などでググると、この設計に関する多くの記事が見つかるでしょう。

ちなみに AWS公式のドキュメント にも、このように記載されています。

In general, you should maintain as few tables as possible in a DynamoDB application. (中略) A single table with inverted indexes can usually enable simple queries to create and retrieve the complex hierarchical data structures required by your application.

こちらは as few table as possible という表現ですね。

DynamoDBは基本的にスキーマレスであり、Itemのスキーマを制限するものはPK, SK, GSI, LSIカラム名・型とTTLカラム名のみです。つまり、それらだけ一致させれば、任意のデータセットを1つのテーブルに集約することが可能です。現実ではカラム名PK / SK など汎用的に命名し、型も文字列型にすることが多いでしょう。そうした条件が満たされていれば任意の2テーブルは1つに集約可能なため、理論上あらゆるアプリケーションはシングルテーブル設計を採ることができるわけです。

技術的にはどちらでも良いが片方を選ばなければならない時、人は悩みます。この記事ではシングルテーブル設計にするか否か、比較の観点をできるだけ網羅してまとめることを目指します。

シングルテーブル設計を検討する観点

観点1. データ取得の性能が改善する場合がある

これが最も定番の理由です。

DynamoDBのQuery APIを利用すると、同じテーブル・同じPartition Key(PK)のデータを、1回のAPIコールでまとめて取得することができます。 これにより、複数のテーブルにデータが散在している場合と比べて、少ない回数のAPIコールで必要な情報を取得でき、効率化できる場合があります。

このメリットはRDBMSのようなJOIN機能を持たないNoSQLに共通の観点であり、 非正規化 という言葉でも有名です。 次のブログなどに詳しい解説があります ( Creating a single-table design with Amazon DynamoDB 。) RDBMSのテーブル設計とは全く異なる独特な考え方が必要のため、慣れないととっつきづらい部分ですが、最近は公式ドキュメントでも多くのユースケースにおけるデザインパターンのベストプラクティスが紹介されているため、少し身近な存在にもなりつつあります。

しかしながら、そもそもまとめて取得する要件がないようなアイテム同士は、一つのテーブル上に配置するメリットがあるとは言えません。 そのようなテーブル同士であっても、一つにまとめるメリットはあるのでしょうか。次を見てみましょう。

観点2. テーブル数が減ると、関連リソース数も減る

運用の都合によっては、DynamoDBテーブルに対して以下のような機能が必要になる場合があります:

  • 各種メトリクスの監視
  • テーブルアクセスに対する監査ログの記録
  • バックアップ
  • 別のストレージへのレプリケーション

これらは利用しているすべてのDynamoDBテーブルに共通で必要になることもあるでしょう。その場合はテーブル数に応じて、関連するCloudWatchのアラームやLambda、DynamoDB Stream、さらにはそれらを監視するアラームなど、管理すべきリソースが増えていきます。

リソースが増えることのデメリットとして、以下が挙げられます:

  1. 監視ダッシュボードやマネジメントコンソールが見にくくなる
  2. 構築が大変になる
  3. 固定費のかかるリソースがある場合は、コスト効率も悪化する

シングルテーブル、あるいはテーブル数を最小化するようにすれば、このリソース増大問題を回避・緩和することができます。実際、私が参加していたプロジェクトでも、この観点が主な理由で10個強のテーブルを1つに集約するようなリファクタを開始していました。

とはいえ2のデメリットについては、CDKなどのIaCを利用すれば比較的容易に必要なリソースを反復定義・デプロイできます。また、3のデメリットについても、サーバーレスのサービスを選べば、固定費が掛かるサービスは少ないでしょう。 このため、こうしたデメリットは許容できる場合も十分考えられます。

観点3. キャパシティ管理のしやすさ

DynamoDBをProvisionedモードで利用する場合、テーブルごとにキャパシティ (WCU, RCUの割当数)の管理が必要になります。

テーブルを一つにまとめるとキャパシティ管理の対象も一つになるので、管理が容易になる場合も多いでしょう。またテーブルをまとめることで、小規模なテーブルのキャパシティを大規模なテーブルのキャパシティの余剰分でまかなうことができるため、コスト効率の面でもシングルテーブルに分があります。この辺りの話は、マルチテナントのリソース共有による効率性の話にも通ずるものがありますね。

Resource sharing is a central benefit in multi-tenant systems. A multi-tenant system handles multiple workloads, such as work from multiple customers at once. This system can also handle low priority, non-urgent workloads along with high-priority, urgent workloads. A single-tenant system, on the other hand, handles workloads from a single customer. Fairness in multi-tenant systems

ただし、今のDynamoDBではキャパシティ管理が(ほぼ)不要なOn-demandモードもサポートされています。この場合は、単純にリクエスト数に応じた課金になるため、テーブルが分かれていても一つでもコストは同じです。従って、On-demandモードで運用する場合は、この観点のメリットは当てはまりません。

とはいえ、サービスの規模が大きい場合などワークロードによっては、On-demandモードよりもProvisionedモードを頑張って運用する方が安くなる場合があります。この分岐点を超えると、On-demandからProvisionedへの移行に踏み切ることもあるでしょう。いずれProvisionedに移行する可能性があるのなら、最初からそれに適したシングルテーブル設計にすべきという考え方もあるかもしれません。

観点4. IAM権限管理のしやすさ

シングルテーブルにすることでIAM権限管理が粗くなるんじゃない?と不安になるかもしれません。しかし、DynamoDBではテーブル単位だけでなく、行単位のIAMアクセス制御も可能です。

Using IAM policy conditions for fine-grained access control - Amazon DynamoDB

これにより、シングルテーブル設計であっても、サービスごとにテーブルを分けているような場合と同等の権限管理を実現できます。

実際のIAMポリシー例は、以下の回答も参考になります。ForAllValues:StringLikedynamodb:LeadingKeys を組み合わせることで、PK が特定のprefixをもつアイテムに対してのみアクセスを許可することができます。

stackoverflow.com

とはいえ、CDKの grantXX 系メソッド はテーブル単位でのみ設定可能、といったように既存のユーティリティがItemレベルの権限管理に十分対応していない事情もあります。この意味では、シングルテーブルにすることでIAM権限管理がやや煩雑になると考えても間違いではないでしょう。

まとめ

DynamoDBのシングルテーブル設計を採るか否かについて、検討ポイントを列挙してました。私自身は、性能に寄与しない場合は無理にシングルテーブルにしなくてよいだろう派ではありましたが、上記の考慮点をケアすると、その結論には至らない場合も多そうです。今後ともケースバイケースで対応していければと思います!

追記

最近(2023年末〜)はシングルテーブル設計に対する懐疑的な意見も多いようです。こちら↓の記事が分かりやすいです (元DynamoDBチームの方による記事)。

Single table design for DynamoDB: The reality — Momento

シングルメリットのデメリットとして、主には以下が挙げられています。

DynamoDBのドキュメントでは今もシングルテーブルが推奨されているため、人により意見の異なるトピックなのでしょう。

In the majority of cases, we recommend that you consider using a single table. source

どちらにしても偏った意見には惑わされず、臨機応変な判断をしていきたいですね。