はじめに:RDBの常識を捨てよ。DynamoDBシングルテーブル設計という革命
リレーショナルデータベース(RDB)に慣れ親しんだエンジニアが、Amazon DynamoDBを初めて使うとき、必ずと言っていいほど同じ過ちを犯します。それは、エンティティごとにテーブルを細かく正規化して作成してしまうことです。
Users
テーブル、Products
テーブル、Orders
テーブル…。一見すると美しく整理されているこの設計は、DynamoDBの世界では、コスト、パフォーマンス、スケーラビリティのすべてを悪化させるアンチパターンとなり得ます。
SaaSアプリケーション、特にマルチテナント環境のデータ永続化において、DynamoDBの真の力を引き出す設計思想、それが「シングルテーブル設計」です。これは、アプリケーションのすべてのエンティティを、たった一つのテーブルに格納する、RDBの常識とは真逆のアプローチです。
この記事を読めば、あなたもシングルテーブル設計の核心を理解し、テナントデータの完全な分離と究極のコスト最適化を両立させる、スケーラブルなデータ基盤を構築できるようになります。
シングルテーブル設計の核心:アクセスパターンに基づいたキー設計
シングルテーブル設計の成否は、すべてプライマリキーの設計にかかっています。RDBのように後から柔軟なクエリを発行するのではなく、まずアプリケーションが必要とする「全アクセスパターン」を事前に洗い出し、そのすべてに効率的に応答できるキー構造を設計します。
-
パーティションキー(PK): テナント分離の要です。マルチテナントSaaSでは、PKに必ずテナントIDを含めるのが鉄則です。一般的には
TENANT#<tenant_id>
のようなプレフィックスを付けた文字列を使います。これにより、あるテナントに関連する全てのデータが、物理的に近い場所に格納され、効率的に取得できます。 -
ソートキー(SK): 1対多の複雑な関係を表現する魔法のキーです。同じテナント(同じPK)の中で、異なる種類のデータ(ユーザー、注文、商品など)を区別し、並べ替えるために使います。
USER#<user_id>
やORDER#<order_id>#ITEM#<item_id>
のように、エンティティの種類とIDを#
で連結して階層構造を持たせるのが一般的なテクニックです。
【実践例】SaaSアプリケーションのデータモデリング
言葉だけでは分かりにくいので、具体的な例を見ていきましょう。テナントA
に所属するユーザーX
が注文1
と注文2
を行った場合のテーブル構造です。
PK (Partition Key) | SK (Sort Key) | データ |
---|---|---|
TENANT#A |
USER#X |
{ "name": "Haru", "email": "..." } |
TENANT#A |
ORDER#1 |
{ "date": "2025-08-05", "amount": 5000 } |
TENANT#A |
ORDER#2 |
{ "date": "2025-08-06", "amount": 3000 } |
この設計により、以下のような複雑なデータ取得が、たった1回のQuery
APIコールで、極めて高速に実行できます。
- 特定テナントの全データを取得:
PK = TENANT#A
を指定してクエリ。 - 特定テナントの全注文を取得:
PK = TENANT#A
かつSKが "ORDER#" で始まる
という条件でクエリ。 - 特定ユーザーのプロフィールと全注文を同時に取得:
PK = TENANT#A
かつSKが "USER#X" または "ORDER#" で始まる
という条件でクエリ。(※実際にはより工夫が必要ですが、概念として)
このように、関連するデータをまとめて取得できるため、RDBで頻発するN+1問題のようなパフォーマンスのボトルネックを根本的に解決できます。
GSI(グローバルセカンダリインデックス)の戦略的活用
プライマリキーだけでは対応できないアクセスパターンも存在します。例えば、「メールアドレスからユーザーを検索したい」といったケースです。このような逆引き検索は、GSI (Global Secondary Index) を作成することで実現します。
ただし、GSIの設計でもテナント分離の原則は変わりません。GSIのパーティションキーにもテナントIDを含めるか、あるいはアプリケーションロジックでテナントIDによる絞り込みを徹底する必要があります。
鉄壁のテナント分離:IAMポリシーによる最終防衛ライン
シングルテーブル設計では、全テナントのデータが同じテーブルに存在するため、セキュリティが懸念されるかもしれません。しかし、IAMポリシーを組み合わせることで、RDB以上に堅牢なデータ分離を実現できます。
アプリケーションの実行ロールにアタッチするIAMポリシーで、DynamoDBのdynamodb:LeadingKeys
条件キーを使用します。これにより、「認証されたユーザーは、自身のテナントID(JWTから取得)で始まるパーティションキーを持つアイテムしか読み書きできない」というルールをデータベースレベルで強制できます。
{
"Effect": "Allow",
"Action": "dynamodb:Query",
"Resource": "arn:aws:dynamodb:*:*:table/YourSaaSTable",
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": [
"TENANT#${cognito:amr[2]}"
]
}
}
}
この設定により、たとえアプリケーションのコードにバグがあり、他のテナントIDを指定してクエリを発行しようとしても、IAMレベルで確実にブロックされます。これこそが、シングルテーブル設計における究極のセキュリティモデルです。
まとめ:シングルテーブル設計は、SaaSの成長を支えるスケーラブルなデータ基盤である
DynamoDBのシングルテーブル設計は、間違いなく初期の学習コストが高いアプローチです。しかし、一度その設計思想をマスターすれば、その見返りは計り知れません。
- 圧倒的なパフォーマンス: 関連データを1回のAPIコールで取得。
- 究極のコスト効率: プロビジョニングしたキャパシティを全テナントで共有し、無駄を排除。
- 無限のスケーラビリティ: DynamoDBの水平スケーリング能力を最大限に活用。
- 堅牢なセキュリティ: IAMポリシーとの組み合わせによる鉄壁のテナント分離。
これらは、SaaSビジネスを成功に導く上で不可欠な要素です。RDBの常識を一度リセットし、シングルテーブル設計という新しいパラダイムを受け入れること。それが、あなたのSaaSの成長を支える、最も合理的でスケーラブルなデータ基盤を築くための第一歩です。
コメント