DynamoDBのテーブルを1つだけにする設計のコツ(汎用的手法編)
DynamoDBではテーブルを1つだけにすることが推奨されています。前回の投稿ではDynamoDBのテーブルを1つだけにする設計のコツを具体例を元に考えてみました。今回はどんな場合にでも通用するような汎用的な手法を考えてみます。
エンティティにIDを追加
エンティティにはユニークなIDを持たせるのが普通です。
前回の例にIDを持たせたデータは以下のようになります。
Artistテーブル
ID | ArtistName | CarrerStart |
---|---|---|
1 | David Bowie | 1962 |
2 | Bryan Adams | 1975 |
3 | Steely Dan | 1972 |
Songテーブル
ID | SongTitle | Artist ID | Released | Album ID | Note |
---|---|---|---|---|---|
1 | Ziggy Stardust | 1 | 1972 | 1 | This is a good so... |
2 | Changes | 1 | 1971 | 2 | |
3 | Sons of the Silent Age | 1 | 1977 | 3 | |
4 | Heroes | 1 | 1977 | 3 | Good |
5 | Cloud Number Nine | 2 | 1988 | 4 | |
6 | Summer of '69 | 2 | 1984 | 5 | |
7 | On a Day Like Today | 2 | 1998 | 4 | |
8 | Reelin' in the Years | 3 | 1972 | 6 | |
9 | Turn That Heartbeat Ov... | 3 | 1972 | 6 | |
10 | Change of the Guard | 3 | 1972 | 6 | |
11 | Deacon Blues | 3 | 1977 | 7 |
Albumテーブル
ID | AlubumTitle | Artist ID | Genre | Studio |
---|---|---|---|---|
1 | The Rise and Fall of Zi... | 1 | Rock | Trident Studios |
2 | Hunky Dory | 1 | Rock | Trident Studios |
3 | Heros | 1 | Rock | Hansa |
4 | On a Day Like Today | 2 | Alternative | The Warehous... |
5 | Reckless | 2 | Rock | Little Mountain... |
6 | Can't Buy a Thrill | 3 | Soft Rock | The Village Rec... |
7 | Aja | 3 | Soft Rock | The Village Rec... |
隣接関係のリスト設計パターン
隣接関係のリスト設計パターンを利用して、PK(パーティションキー)にエンティティID、SK(ソートキー)にリレーションのあるエンティティIDを入れます。
エンティティIDは[エンティティ名]-[ユニークなID]という形式にします。IDの採番はDynamoDBではオートインクリメントがUUIDを使えばいいです。putする前にそのIDが使われていないかはチェックする必要があります。
PK | SK |
---|---|
Artist-1 | Song-1 |
Artist-1 | Song-2 |
Artist-1 | Song-3 |
Artist-1 | Song-4 |
Artist-1 | Album-1 |
Artist-1 | Album-2 |
Artist-1 | Album-3 |
Artist-2 | Song-5 |
Artist-2 | Song-6 |
Artist-2 | Song-7 |
Artist-2 | Album-4 |
Artist-2 | Album-5 |
Artist-3 | Song-8 |
Artist-3 | Song-9 |
Artist-3 | Song-10 |
Artist-3 | Song-11 |
Artist-3 | Album-6 |
Artist-3 | Album-7 |
グローバルセカンダリインデックスGSI1をテーブルのPKをプライマリキー、テーブルのSKをソートキーとして作ります。
こうすることで多対多の関係であっても、エンティティIDを指定することでリレーションのあるエンティティを取り出すことができるようになります。
例えば、テーブルのPK='Artist-1'、SK=begins_with('Song-')を指定すれば、Artist-1のSongを取り出すことができます。逆にGSI1のPK='Song-1'、SK=begins_with('Artist-')を指定すれば、Song-1のArtistを取りですことができます。
クエリの洗い出し
次にアプリケーションに使うクエリを考えます。
前回と同じく、以下のようなクエリーがアプリケーションに必要だとします。
- アーティスト名を指定してすべての曲を見つける
- ジャンルを指定してすべてのアルバムを見つける
- リリース年とアーティスト名から曲を検索する
- 曲名から同じ曲名の曲を検索する
グローバルセカンダリインデックスの多重定義
グローバルセカンダリインデックスの多重定義を利用します。
検索に使う項目をテーブルにSKに、その項目の値をDataカラムに入れます。
検索に使う項目は以下のとおりです。
- Artistのアーティスト名
- Albumのジャンル
- Songのアーティスト名とリリース年
- Songの曲名
SKに入れる項目名は[エンティティ名]_[項目名]という形式にします。
PK(GSI1のSK) | SK(GSI1のPK) | Data |
---|---|---|
Artist-1 | Artist_Name | David Bowie |
Artist-1 | Song-1 | |
Artist-1 | Song-2 | |
Artist-1 | Song-3 | |
Artist-1 | Song-4 | |
Artist-1 | Album-1 | |
Artist-1 | Album-2 | |
Artist-1 | Album-3 | |
Artist-2 | Artist_Name | Bryan Adams |
Artist-2 | Song-5 | |
Artist-2 | Song-6 | |
Artist-2 | Song-7 | |
Artist-2 | Album-4 | |
Artist-2 | Album-5 | |
Artist-3 | Artist_Name | Steely Dan |
Artist-3 | Song-8 | |
Artist-3 | Song-9 | |
Artist-3 | Song-10 | |
Artist-3 | Song-11 | |
Artist-3 | Album-6 | |
Artist-3 | Album-7 | |
Song-1 | Song_ArtistName-Released | David Bowie_1972 |
Song-1 | Song_Name | Ziggy Stardust |
Song-2 | Song_ArtistName-Released | David Bowie_1971 |
Song-2 | Song_Name | Changes |
Song-3 | Song_ArtistName-Released | David Bowie_1977 |
Song-3 | Song_Name | Sons of the Silent Age |
Song-4 | Song_ArtistName-Released | David Bowie_1977 |
Song-4 | Song_Name | Heroes |
Song-5 | Song_ArtistName-Released | Bryan Adams_1988 |
Song-5 | Song_Name | Cloud Number Nine |
Song-6 | Song_ArtistName-Released | Bryan Adams_1984 |
Song-6 | Song_Name | Summer of '69 |
Song-7 | Song_ArtistName-Released | Bryan Adams_1998 |
Song-7 | Song_Name | On a Day Like Today |
Song-8 | Song_ArtistName-Released | Steely Dan_1972 |
Song-8 | Song_Name | Reelin' in the Years |
Song-9 | Song_ArtistName-Released | Steely Dan_1972 |
Song-9 | Song_Name | Turn That Heartbeat Ov... |
Song-10 | Song_ArtistName-Released | Steely Dan_1972 |
Song-10 | Song_Name | Change of the Guard |
Song-11 | Song_ArtistName-Released | Steely Dan_1977 |
Song-11 | Song_Name | Deacon Blues |
Album-1 | Album_Genre | Rock |
Album-2 | Album_Genre | Rock |
Album-3 | Album_Genre | Rock |
Album-4 | Album_Genre | Alternative |
Album-5 | Album_Genre | Rock |
Album-6 | Album_Genre | Soft Rock |
Album-7 | Album_Genre | Soft Rock |
グローバルセカンダリインデックスGSI2をテーブルのSKをプライマリキー、テーブルのDataカラムをソートキーとして作ります。テーブルを見て分かる通り、Dataカラムはリレーションについては何も入っておらず、スパースなインデックスになっています。
例えば、GSI2のPK='Artist_Name'、SK='David Bowie'とすることで、David BowieのArtistID='Artist-1'を見つけられます。次に、テーブルのPK='Artist-1'、SK=begins_with('Song-')を指定すれば、David Bowieの曲のSong IDを取り出すことができます。
また、Song_ArtistName-Releasedという項目名をつくり、アーティスト名とリリース年の複合キーワードにも対応しています。
残りの項目
残りの項目は自分自身のエンティティIDをテーブルのSKに入れて、定義します。
PK(GSI1のSK) | SK(GSI1のPK、GSI2のPK) | Data(GSI2のSK) | CareerStart | Released | Studio |
---|---|---|---|---|---|
Artist-1 | Artist-1 | 1962 | |||
Artist-1 | Artist_Name | David Bowie | |||
Artist-1 | Song-1 | ||||
Artist-1 | Song-2 | ||||
Artist-1 | Song-3 | ||||
Artist-1 | Song-4 | ||||
Artist-1 | Album-1 | ||||
Artist-1 | Album-2 | ||||
Artist-1 | Album-3 | ||||
Artist-2 | Artist-2 | 1975 | |||
Artist-2 | Artist_Name | Bryan Adams | |||
Artist-2 | Song-5 | ||||
Artist-2 | Song-6 | ||||
Artist-2 | Song-7 | ||||
Artist-2 | Album-4 | ||||
Artist-2 | Album-5 | ||||
Artist-3 | Artist-3 | 1972 | |||
Artist-3 | Artist_Name | Steely Dan | |||
Artist-3 | Song-8 | ||||
Artist-3 | Song-9 | ||||
Artist-3 | Song-10 | ||||
Artist-3 | Song-11 | ||||
Artist-3 | Album-6 | ||||
Artist-3 | Album-7 | ||||
Song-1 | Song-1 | 1972 | |||
Song-1 | Song_ArtistName-Released | David Bowie_1972 | |||
Song-1 | Song_Name | Ziggy Stardust | |||
Song-2 | Song-2 | 1971 | |||
Song-2 | Song_ArtistName-Released | David Bowie_1971 | |||
Song-2 | Song_Name | Changes | |||
Song-3 | Song-3 | 1977 | |||
Song-3 | Song_ArtistName-Released | David Bowie_1977 | |||
Song-3 | Song_Name | Sons of the Silent Age | |||
Song-4 | Song-4 | 1977 | |||
Song-4 | Song_ArtistName-Released | David Bowie_1977 | |||
Song-4 | Song_Name | Heroes | |||
Song-5 | Song-5 | 1988 | |||
Song-5 | Song_ArtistName-Released | Bryan Adams_1988 | |||
Song-5 | Song_Name | Cloud Number Nine | |||
Song-6 | Song-6 | 1984 | |||
Song-6 | Song_ArtistName-Released | Bryan Adams_1984 | |||
Song-6 | Song_Name | Summer of '69 | |||
Song-7 | Song-7 | 1998 | |||
Song-7 | Song_ArtistName-Released | Bryan Adams_1998 | |||
Song-7 | Song_Name | On a Day Like Today | |||
Song-8 | Song-8 | 1972 | |||
Song-8 | Song_ArtistName-Released | Steely Dan_1972 | |||
Song-8 | Song_Name | Reelin' in the Years | |||
Song-9 | Song-9 | 1972 | |||
Song-9 | Song_ArtistName-Released | Steely Dan_1972 | |||
Song-9 | Song_Name | Turn That Heartbeat Ov... | |||
Song-10 | Song-10 | 1972 | |||
Song-10 | Song_ArtistName-Released | Steely Dan_1972 | |||
Song-10 | Song_Name | Change of the Guard | |||
Song-11 | Song-11 | 1977 | |||
Song-11 | Song_ArtistName-Released | Steely Dan_1977 | |||
Song-11 | Song_Name | Deacon Blues | |||
Album-1 | Album-1 | Trident Studios | |||
Album-1 | Album_Genre | Rock | |||
Album-2 | Album-2 | Trident Studios | |||
Album-2 | Album_Genre | Rock | |||
Album-3 | Album-3 | Hansa | |||
Album-3 | Album_Genre | Rock | |||
Album-4 | Album-4 | The Warehous... | |||
Album-4 | Album_Genre | Alternative | |||
Album-5 | Album-5 | Little Mountain... | |||
Album-5 | Album_Genre | Rock | |||
Album-6 | Album-6 | The Village Rec... | |||
Album-6 | Album_Genre | Soft Rock | |||
Album-7 | Album-7 | The Village Rec... | |||
Album-7 | Album_Genre | Soft Rock |
これで完成です。
テーブル1つにする設計の汎用的手法
これまでの例で見てきたことをまとめます。
- 隣接関係のリスト設計パターンを利用して、PKにエンティティID、SKに関連のあるエンティティIDを入れる
- テーブルのSKをPK、テーブルのPKをSKにしたグルーバルセカンダリインデックスをつくる
- クエリを洗い出す
- テーブルのSKにクエリ検索に使用する項目、テーブルのDataカラムにその項目の値を入れる
- テーブルのSKをPK、テーブルのDataカラムをSKとした多重定義なグルーバルセカンダリインデックスをつくる
- テーブルのPKと同じエンティティIDをSKに入れて、そのエンティティの属性を対応するカラムに入れる
- エンティティID、SKに入れる項目名は被らないようにルールを決める
クエリによってはこれだけで対応できず、グローバルセカンダリインデックスやローカルセカンダリインデックスが必要になることもあると思います。その時は臨機応変にセカンダリインデックスを追加してください。また、項目の重複は恐れずに、クエリが効率化するようなスキーマになるように設計しましょう。