@mizumotokのブログ

テクノロジー、投資、書評、映画、筋トレなどについて

DynamoDBのテーブルを1つだけにする設計のコツ(考え方編)

DynamoDBは高速でスケーラブルでとても便利なデータベースです。便利なのですが、テーブル設計にはRDBMSのノウハウをそのまま使えません。例えば、DynamoDBではテーブルを1つだけにすることが推奨されています。テーブルを1つだけにする設計手法について考えてみましょう。

f:id:mizumotok:20190813172249j:plain

DynamoDBの特長

DynamoDBはAWS(アマゾンウェブサービス)で提供される、とても便利なNoSQL データベースサービスです。高速なパフォーマンスとシームレスなスケーラビリティが特長です。
RDBMSではディスク容量が足りなくなったりしたら、基本的にはディスクを増やす等の「スケールアップ(リソースを一箇所に増やす)」をするという運用が発生します。この運用も頻繁にするのは面倒くさいので最初から大きめのディスク容量、CPU、メモリをとっておくことになります。
DynamoDBでは、データ量や読み書きの頻度に応じて自動的に「スケールアウト(リソースを複数箇所に増やしてアクセスを分散化)」してくれるのです。最初は最小限の費用から始められて、サービスが成長しても自動的に対応してくれるので、運用が非常に楽になります。

できるだけ少ないテーブル数で

DynamoDBの「開発者ガイド>ベストプラクティス>DynamoDB に合わせた NoSQL設計」を読んでいると衝撃の考え方が出てきます。

DynamoDB アプリケーションではできるだけ少ないテーブルを維持する必要があります。設計が優れたアプリケーションでは、必要なテーブルは 1 つのみです。

スケールアップはテーブル単位で行われます。テーブルを分割するとテーブル毎にデータ量やアクセス頻度が違うので、あるテーブルはスケールアウトされるがあるテーブルはスケールアウトされないままになったりします。キャパシティ管理が難しくなるのです、アプリケーション全体で容量やアクセス頻度を効率よく使うためには、テーブル数を少なくすることで、DynamoDBのリソースを効率良く使うことができるのです。
NoSQLデータベースはスキーマレスなので、どんなデータ構造でも入れることができ、つまりは1つのテーブルでどんなデータ構造でも対応できてしまうのです。

RDBMSで正規化を繰り返してテーブルを増やしていく設計思想とは真逆なのです。これははじめ何をどうしていいのか分かりませんでした。

1つのテーブルでの設計例

開発者ガイドにも「多対多の関係を管理するためのベストプラクティス」「DynamoDB でリレーショナルデータをモデル化するためのベストプラクティス」があってテーブル1つでリレーショナル関係を設計する方法が書いてあるのですが、これだけ読んでもよくわからないです。

www.edx.org
この動画講座の「Week 4 > Schema Design > Single Table」を見ると1テーブルでの設計例があり、理解が深まりました。

データの収集

例えばArtist、Song、Albumの情報が以下のようにテーブル構造になっていたとします。

Artist CarrerStart
David Bowie 1962
Bryan Adams 1975
Steely Dan 1972
SongTitle Artist Released AlbumTitle Note
Ziggy Stardust David Bowie 1972 The Rise and Fall of Zi... This is a good so...
Changes David Bowie 1971 Hunky Dory
Sons of the Silent Age David Bowie 1977 Heroes
Heroes David Bowie 1977 Heroes Good
Cloud Number Nine Bryan Adams 1988 On a Day Like Today
Summer of '69 Bryan Adams 1984 Reckless
On a Day Like Today Bryan Adams 1998 On a Day Like Today
Reelin' in the Years Steely Dan 1972 Can't Buy a Thrill
Turn That Heartbeat Ov... Steely Dan 1972 Can't Buy a Thrill
Change of the Guard Steely Dan 1972 Can't Buy a Thrill
Deacon Blues Steely Dan 1977 Aja
AlubumTitle ArtistName Genre Studio
The Rise and Fall of Zi... David Bowie Rock Trident Studios
Hunky Dory David Bowie Rock Trident Studios
Heros David Bowie Rock Hansa
On a Day Like Today Bryan Adams Alternative The Warehous...
Reckless Bryan Adams Rock Little Mountain...
Can't Buy a Thrill Steely Dan Soft Rock The Village Rec...
Aja Steely Dan Soft Rock The Village Rec...

クエリの洗い出し

RBMSではデータ構造を考えるときに正規化を粛々と進めていくのですが、NoSQLではアプリケーションに使うクエリを考えます。
例えば以下のようなクエリーがアプリケーションに必要だとします。

  1. アーティスト名を指定してすべての曲を見つける
  2. ジャンルを指定してすべてのアルバムを見つける
  3. リリース年とアーティスト名から曲を検索する
  4. 曲名から同じ曲名の曲を検索する

1つのテーブルにする

NoSQLはスキーマレスなので1つのテーブルにするのは簡単ですが、注意しなくてはいけないのはPartition KeyとSort Keyを何にするかです。

まずAristテーブルから考えましょう。アーティスト名をキーにできればいいので、ArtistNameをPartition Keyにします。ここでコツがあって、Sort KeyにもArtistNameを入れておきます。

Partition Key Sort Key CareerStart Released Genre Album Studio
David Bowie David Bowie 1962
Bryan Adams Bryan Adams 1975
Steely Dan Steely Dan 1972

次にSongテーブルを考えます。ArtistNameをPartition Keyにしたので、Sort KeyにSongTitleを入れましょう。

Partition Key Sort Key CareerStart Released Genre Album Studio
David Bowie David Bowie 1962
David Bowie Ziggy Stardust 1972 The Rise and Fall of Zi...
David Bowie Changes 1971 Hunky Dory
David Bowie Sons of the Silent Age 1977 Heroes
David Bowie Heroes 1977 Heroes
Bryan Adams Bryan Adams 1975
Bryan Adams Cloud Number Nine 1988 On a Day Like Today
Bryan Adams Summer of '69 1984 Reckless
Bryan Adams On a Day Like Today 1988 On a Day Like Today
Steely Dan Steely Dan 1972
Steely Dan Reelin' in the Years 1972 Can't Buy a Threill
Steely Dan Turn That Heartbeat Ov... 1972 Can't Buy a Threill
Steely Dan Change of the Guard 1972 Can't Buy a Threill
Steely Dan Deacon Blues 1977 Aja

最後にAlbumテーブルの情報を入れます。ArtistNameをPartition Keyにしたので、Sort KeyにAlbumTitleを入れましょう。

Partition Key Sort Key CareerStart Released Genre Album Studio
David Bowie David Bowie 1962
David Bowie Ziggy Stardust 1972 The Rise and Fall of Zi...
David Bowie Changes 1971 Hunky Dory
David Bowie Sons of the Silent Age 1977 Heroes
David Bowie Heroes 1977 Heroes
David Bowie The Rise and Fall of Zi... Rock Trident Studios
David Bowie Hunky Dory Rock Trident Studios
David Bowie Heros Rock Hansa
Bryan Adams Bryan Adams 1975
Bryan Adams Cloud Number Nine 1988 On a Day Like Today
Bryan Adams Summer of '69 1984 Reckless
Bryan Adams On a Day Like Today 1988 On a Day Like Today
Bryan Adams On a Day Like Today Alternative The Warehous...
Bryan Adams Reckless Rock Little Mountain...
Steely Dan Steely Dan 1972
Steely Dan Reelin' in the Years 1972 Can't Buy a Thrill
Steely Dan Turn That Heartbeat Ov... 1972 Can't Buy a Threill
Steely Dan Change of the Guard 1972 Can't Buy a Thrill
Steely Dan Deacon Blues 1977 Aja
Steely Dan Can't Buy a Thrill Soft Rock The Village Rec...
Steely Dan Aja Soft Rock The Village Rec...

これでテーブルはできあがりです。正規化とは対立する手法ですね。

クエリーにあわせてセカンダリーインデックス

「アーティスト名を指定してすべての曲を見つける」

これは簡単です。
Prtition KeyにAristNameが入っているので、Releasedが入っているレコードを見つけてくればいいだけです。

「ジャンルを指定してすべてのアルバムを見つける」

GenreをPatrition Keyとしたグローバルセカンダリインデックスを作ります。Genreカラムが入っているレコードのSort Keyがアルバムになります

「リリース年とアーティスト名から曲を検索する」

アーティスト名はPartition Keyに使われていますので、ReleasedをSort Keyとしたローカルセカンダリインデックスを作ればいけます。Partition Keyはすでにあるのでローカルセカンダリインデックスはなくてもいいのですが、曲名以外にはRelease属性はついていないためインデックスの容量が少なくてすみ、検索がとても効率的に働きます。こういうインデックスをスパースなインデックスといい、DynamoDBで推奨されています。

「曲名から同じ曲名の曲を検索する」

曲名(=Sort Key)をPatrition Keyとしたグローバルセカンダリインデックスを作ります。Releasedが入っているレコードを見つけてくればいいでしょう。

最終的に1つのテーブル、2つのグルーバルセカンダリインデックス、1つのローカルセカンダリインデックスをつくれば、クエリー要件は満たすことができます。

設計を汎用的に

上述の例を考えると、Partition Keyに一番重要そうなテーブルのプライマリキー、Sort Keyにそのテーブルとリレーショナルなテーブルのプライマリキーを入れることで実現できます。Partition KeyとSort Keyだけを見るとテーブルの関係性を示しています。この設計手法を隣接関係のリスト設計パターンと言います。
実際このやり方で1つのテーブルに収めればいいのですが、Partition Keyにはクエリーで使用する文字列でなくユニークなIDを入れたくなるケースがあります。
例えば、アーティス名はユニークであるとは限りません。アーティスト名で検索してアーティストIDを見つけて、それからアーティストIDで曲名を検索するようなステップが必要です。その場合、アーティストIDをPartition Keyにしたテーブルを作成し、アーティスト名をPartition Keyにしたグローバルセカンダリインデックスをつくることになります。このあたりを考慮した、もう少し汎用的な設計手法を次の投稿で書きたいと思います。

mizumotok.hatenablog.jp

まとめ

  • DynamoDBは高速でスケーラブルはNoSQLデータベース
  • テーブルはできるだけ少なく、理想は1つだけ
  • 必要なクエリを洗い出してから、テーブルの構造を決める
  • Partition Keyに一番重要そうなテーブルのプライマリキー、Sort Keyにリレーショナルなテーブルのプライマリキーを入れるのがコツ(隣接関係のリスト設計パターン)
  • クエリが効率的に働くようなセカンダリインデックスをつける

RDB技術者のためのNoSQLガイド

RDB技術者のためのNoSQLガイド