本記事は、AI を活用して執筆しています。
1. はじめに
DynamoDB のテーブル設計を AI 開発支援ツール (Rovo Dev) に任せたところ、PK と SK に USER#123456789 のような形式が提案された。
RDB に慣れている身としてはやや驚いたが、これは DynamoDB における一般的な設計パターンらしい。
2. USER#123456789 形式に感じた違和感
提案されたキー構造の一例は、以下の通りである。
PK: USER#123456789
SK: PROFILE
また別の例では、以下のような形もあった。
PK: USER#123456789
SK: ORDER#20240625
一見すると文字列に余計な情報が混ざっているように見えるが、この接頭辞付きの構造には意味がある。 これは、DynamoDB の「シングルテーブル設計」に基づいたものであり、ID にエンティティの種別を含めることでデータの階層性・意味性を保持している。
3. DynamoDB の設計思想とシングルテーブル設計
DynamoDB はリレーショナルデータベースではないため、JOIN や正規化といった概念が存在しない。 その代わりに「どのように使うか」(アクセスパターン)に合わせて、データ構造を最適化する必要がある。
シングルテーブル設計とは、1 つのテーブルに複数種類のエンティティを格納し、PK と SK で関連付ける設計手法である。 これにより、あるエンティティに関連するすべての情報を一括で取り出せるというメリットがある。
4. 実例:PK/SK に意味を持たせた設計パターン
たとえば、ユーザー情報と注文履歴を同じテーブルに格納する場合、以下のように設計する。
| PK | SK | 内容 |
|---|---|---|
| USER#123456789 | PROFILE | ユーザーの基本情報 |
| USER#123456789 | ORDER#20240625 | 注文履歴(1 件目) |
| USER#123456789 | ORDER#20240626 | 注文履歴(2 件目) |
この構造により、あるユーザーに紐づくデータを以下のように一発で取得できる。
クエリ例:特定ユーザーのデータを取得する
{
"TableName": "MainTable",
"KeyConditionExpression": "PK = :pk",
"ExpressionAttributeValues": {
":pk": { "S": "USER#123456789" }
}
}
これにより、PROFILE とすべての ORDER がまとめて取得できる。
5. GSI と組み合わせた柔軟な検索設計
PK/SK 構造だけでは対応できない検索パターンも存在する。 たとえば「2024 年 6 月 25 日に行われたすべての注文を取得したい」といったケースである。
このようなときは、GSI (グローバルセカンダリインデックス) を使用する。
GSI の設計例
| PK | SK | GSI1PK | GSI1SK |
|---|---|---|---|
| USER#123456789 | ORDER#20240625 | ORDER_DATE#20240625 | ORDER#20240625 |
| USER#987654321 | ORDER#20240625 | ORDER_DATE#20240625 | ORDER#20240625 |
| USER#123456789 | ORDER#20240626 | ORDER_DATE#20240626 | ORDER#20240626 |
クエリ例:指定日付の注文を取得する
{
"TableName": "MainTable",
"IndexName": "GSI1",
"KeyConditionExpression": "GSI1PK = :date",
"ExpressionAttributeValues": {
":date": { "S": "ORDER_DATE#20240625" }
}
}
このようにして、ユーザーをまたいだ注文の横断的な検索が可能になる。
6. シングルテーブル設計のメリット・デメリット
| 項目 | メリット | デメリット |
|---|---|---|
| クエリ効率 | 関連データを一括で取得可能 | 柔軟な検索には不向き |
| テーブル管理 | テーブルが 1 つで済む | 構造が複雑化しやすく保守性が下がる |
| 拡張性 | GSI を組み合わせることで複数の軸で検索可能 | アクセスパターン追加時に設計変更が必要 |
| コスト管理 | テーブル数を減らしてコストを抑えやすい | 読み取り量や GSI 数次第で逆にコストが上がる |
7. どんなときに向いているか?
シングルテーブル設計は、以下のような場面に適している。
- アクセスパターンがあらかじめ明確になっている
- ユーザー単位など、1 対多のデータ構造が主である
- 書き込みよりも読み込みの効率を重視する
- スキーマ変更が少ないアプリケーション
一方で、検索パターンが流動的なシステムや、スキーマが頻繁に変わるシステムには適さない。
8. おわりに
USER#123456789 のような PK 構造は、冗長に見えるかもしれないが、DynamoDB では標準的な設計である。
エンティティを意味づけし、構造化することで、アクセスパターンを効率化し、JOIN ができないという制約を補っている。
DynamoDB を活用する際には、データをどう保存するかよりも、どう取り出すかを先に考えるべきである。 そのうえで、こうした構造を理解し、適切に使いこなすことが、スケーラブルな設計につながる。
個人的に作っているアプリとかでは、無料 DB として DynamoDB を使いがちなので、今後も活用していきたい。