検索性の高いAmazon DynamoDBのテーブル設計

に公開

やりたいこと

Amazon DynamoDBはNoSQLデータベースサービスである。DynamoDBでは、パーティションキーやセカンダリーキーに対して完全一致もしくは前方一致のみでのデータ取得となる。そのためしかしRDSなどのリレーショナルデータベースと異なり、検索や並び替えなどに関して考慮した設計が必要となります。
以下の記事を参考に行った検索性の高いDynamoDBのテーブル設計について記載します。
https://serverless.co.jp/blog/4jr_j0b1g_9/

設計

参考ページと同様にブログ記事を元に以下の内容になります。

ブログ ←投稿-ユーザー
↑
コメント

ER図の作成

テーブル設計

  • できる限り1つのテーブルにまとめる
  • PK-SKに対して1つのValueをもつ
  • CRUDによるデータ取得を想定

Key-Valueの設定

  • PK(パーティションキー)を<エンティティ名>#<ID>とする
  • SK(ソートキー)を<エンティティ名>#<キー>とする
  • GSI(グローバスセカンダリインデックス)を<エンティティ名>とする
  • Valueに各項目の値をもつ

ソートの設計

  • PK(パーティションキー)を<エンティティ名>#<ID>とする
  • SK(ソートキー)を<エンティティ名>#<ソート名>とする
  • GSI(グローバスセカンダリインデックス)を<エンティティ名>とする
  • Value:<key><Value>#<ソート名><Value> とする(記事投稿順の場合、author_<ユーザーID>#publishDt_<公開日> )

(例)作成されるテーブル

PK SK Value
POST#aaaa-aaaa-aaaa POST#aaaa-aaaa-aaaa
POST#aaaa-aaaa-aaaa POST#title タイトルA
POST#aaaa-aaaa-aaaa POST#content 本文A
POST#aaaa-aaaa-aaaa POST#published_at 2024-01-01
POST#aaaa-aaaa-aaaa POST#sortPublishDtByAuthor author_1111-1111-1111#publishDt_2024-01-01
POST#bbbb-bbbb-bbbb POST#bbbb-bbbb-bbbb
POST#bbbb-bbbb-bbbb POST#title タイトルB
POST#bbbb-bbbb-bbbb POST#content 本文B
POST#bbbb-bbbb-bbbb POST#published_at 2024-01-02
POST#bbbb-bbbb-bbbb POST#sortPublishDtByAuthor author_1111-1111-1111#publishDt_2024-01-02
USER#aaaa-aaaa-aaaa USER#user_id
USER#aaaa-aaaa-aaaa USER#username ユーザー名A
USER#aaaa-aaaa-aaaa USER#email aaaa-aaaa-aaaa@example.com
USER#aaaa-aaaa-aaaa USER#created_at 2024-01-01
COMMENT#aaaa-aaaa-aaaa COMMENT#comment_id
COMMENT#aaaa-aaaa-aaaa COMMENT#post_id aaaa-aaaa-aaaa
COMMENT#aaaa-aaaa-aaaa COMMENT#user_id aaaa-aaaa-aaaa
COMMENT#aaaa-aaaa-aaaa COMMENT#content コメントA
COMMENT#aaaa-aaaa-aaaa COMMENT#published_at 2024-01-02

データ取得

以下に各項目のデータ返却手順の案です。

POST一覧取得の手順

クエリ

GSI:post

その結果以下のレコードを取得

POST#aaaa-aaaa-aaaa / POST#aaaa-aaaa-aaaa
POST#aaaa-aaaa-aaaa / POST#title (タイトルA)
POST#aaaa-aaaa-aaaa / POST#content (本文A)
POST#aaaa-aaaa-aaaa / POST#sortPublishDt (publishDt_20210403)
POST#aaaa-aaaa-aaaa / POST#sortPublishDtByAuthor
POST#bbbb-bbbb-bbbb / POST#bbbb-bbbb-bbbb
...

PK毎に5つのレコードをまとめることで1つの記事のデータとして返却

POSTの手順

クエリ

PK: POST#aaaa-aaaa-aaaa
SK: begins_with('POST')

その結果以下のレコードを取得

POST#aaaa-aaaa-aaaa / POST#aaaa-aaaa-aaaa
POST#aaaa-aaaa-aaaa / POST#title (タイトルA)
POST#aaaa-aaaa-aaaa / POST#content (本文A)
POST#aaaa-aaaa-aaaa / POST#sortPublishDt (publishDt_20210403)
POST#aaaa-aaaa-aaaa / POST#sortPublishDtByAuthor
...

この5つのレコードをまとめることで1つの記事のデータとして返却

記事投稿順の取得

クエリ

SK:begin_with("author_1111-1111-1111#")
GSI:post

POST同様、PK毎にデータをまとめる

実際に利用してみて

これまでのやり方

これまでは以下のように同じIDを持つ内容が「複数のアイテムとして保存」していた。

PK SK ID title content published_at
POST aaaa-aaaa-aaaa タイトルA 本文A 2024-01-01
  • 検索などをデータベース設計時に想定する必要があった。
  • 要件変更によって検索ができない場合、scanfilterなどを行っていた。
  • 必要な情報を取得するためにオーバーフェッチ的になっていた。
  • ソート順をデータ取得後の処理で行う必要があった。

良かった点

  • ER図を作成した時点でDB設計が完了できる。
  • 要件変更があった場合も対応が比較的容易。
  • データ取得時にソート処理ができる。
  • DynamoDBのscanによる負荷、コスト増を減らせる。

悪い点

  • データ取得後にまとめる手間が生まれる

今後

GraphQLにおいても同様にデータ取得できるか確認したい。

Discussion