1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DynamoDB のマルチ属性複合キーを試してみる。

1
Posted at

はじめに

re:Invent 直前ですね!

ここ数日、 AWS の What's New も更新がなくなり、いよいよ本番を待つのみ!といった感じでしょうか?

今回は DynamoDB の以下のアップデートを紹介してみます。

DynamoDB のマルチ属性複合キーにより、GSI で最大 8 属性(パーティションキー: 4、ソートキー: 4)を組み合わせて使用できるようになりました。という更新です。

これまで複数の属性でソート、検索を実施する場合は、各属性を文字列結合した属性を用意する必要がありましたが、これが不要になるアップデートです。

DynamoDB の簡単なおさらい

私のような、長年 RDS のみ使っていた人間からすると、DynamoDB の用語や制限に少し戸惑うので、一旦簡単におさらいします。

今回は以下のようなゲームのハイスコアを管理するテーブルを例に、DynamoDB のインデックス構造を確認していきます。

テーブル構成例

DynamoDB では、以下のようなデータの集まりを「テーブル」と呼びます。

また、1行を「アイテム」、各列(UserIdGameName など)を「属性」と呼びます。

UserId GameName HighScore ScoreDate
user1 PuzzleQuest 15000 2025-01-01
user1 SpaceShooter 28000 2025-01-02
user1 RacingChamp 22000 2025-01-03
user2 PuzzleQuest 18000 2025-01-01
user2 SpaceShooter 32000 2025-01-02

なお、属性はアイテムによって異なる構成にすることも可能です。

プライマリキー(Primary Key)

DynamoDB のテーブルには必ずアイテムを一意に識別できるプライマリキーが必要です。

プライマリキーは、パーティションキー(Partition Key)とソートキー(Sort Key)で構成されます。

パーティションキーのみ

1 つの属性(パーティションキー)で構成するケースです。

パーティションキーでの検索は完全一致(=)のみで、部分一致や範囲検索はできません。

DynamoDB はパーティション単位でスループット制限を設けており、特定のパーティションにアクセスが集中する「ホットパーティション」を避け、スロットリングを防ぐ必要があります。

このため、パーティションキーはワークロードの特性に合わせた項目選定が必要になります。

今回の例だと、UserIdGameNameなどが候補になると思います。

パーティションキー + ソートキー

2 つの属性(パーティションキー、ソートキー)で構成するケースです。

ソートキーは名前の通り、パーティションの中でのデータ順を決めるキーとなります。

ソートキーがあると部分一致や、範囲検索など柔軟な検索が可能になり、欲しい情報がピンポイントで取得可能となります。

逆にソートキーがないと、同一パーティションキーのデータを全て取得し、その中から欲しい情報をスキャンする形となります。大量にデータがある場合、処理速度やコスト面で問題が出てくる可能性があります。

user1パーティションから、SpaceShooterのハイスコアを取得した場合などに利用し、このケースだとGameNameをソートキーにします。

またソートキーは1属性しか指定できないため、ソートキーとは別の条件でソート(検索)したい場合は、 LSI/GSI といった機能を利用します。

LSI (Local Secondary Index)

LSI は、テーブル作成時に指定したパーティションキーを使用し、異なるソートキーでデータをクエリできるインデックスです。

UserId(パーティションキー)+ GameName(ソートキー)で作成したテーブルの場合、LSI で UserId(パーティションキー)+ ScoreDate(ソートキー)を作成することで、スコア記録日時でソートされたクエリが可能になります。

ただし、 LSI はテーブル作成時のみ作成可能で、後から追加できません。

GSI (Global Secondary Index)

GSI は、LSI の制限であった、同じパーティションキーを利用するといった制限がなく、異なるパーティションキーとソートキーを指定できるインデックスです。

ベーステーブルが UserId(パーティションキー)+ GameName(ソートキー)の場合、GSI で GameName(パーティションキー)+ ScoreDate(ソートキー)を作成することで、ゲーム名ごとのハイスコアを日付順でクエリできます。

ただし、GSIはLSIと比べ、整合性やコスト面で考慮点があります。(今回は割愛します)

Composite Key(キー結合)

ソートキーはこれまで同時に複数の属性を指定することはできませんでした。

この対策として、検索条件に利用したい属性を文字列結合することで、一つの属性として扱う方法があります。

例えば、GameNameScoreDateを使ってデータを取得したい場合、以下のように結合キーGameDateKeyを作成します。

UserId GameName HighScore ScoreDate GameDateKey
user1 PuzzleQuest 15000 2025-01-01 PuzzleQuest#20250101
user1 SpaceShooter 28000 2025-01-02 SpaceShooter#20250102
user1 RacingChamp 22000 2025-01-03 RacingChamp#20250103
user2 PuzzleQuest 18000 2025-01-01 PuzzleQuest#20250101
user2 SpaceShooter 32000 2025-01-02 SpaceShooter#20250102

GameDateKey という属性を追加し、GameNameScoreDate# で結合した値を格納します。

この GameDateKey を GSI のパーティションキーまたはソートキーとして指定することで、ゲーム名と日付の組み合わせで検索できます。

ただ、アプリ側でキー結合が必要だったり、データの整合性に課題がありそうですね。

...ということで、今回のアップデートは今まで一つしか指定できないパーティションキー、ソートキーを複数指定できるようなり、ホットパーティションの軽減や、 複数属性を使った検索ができるようになりました!という内容です。

新機能! マルチ属性複合キーを試してみる。

では、実際に試してみます。

今回はマネジメントコンソールで実施し、先ほど例に挙げてたゲームスコアテーブルに対して、マルチ属性複合キーを試してみます。

テーブル、アイテム作成

まずはこれまでの例の通り、 UserId(パーティションキー)+ GameName(ソートキー)でテーブルを作ります。

image.png

手動で 5 件分のデータも作成しておきます。

image-1.png

これで、 UserId(パーティションキー)+ GameName(ソートキー)を使った検索が可能です。

image.png

この状態で、フィルタ機能を使い、ScoreDate2025-01-01のデータを取得しようとすると、取得はできますが、スキャンされた項目: 3となっており、user1パーティションのデータを全て取得していることがわかります。

image-2.png

そこで、次に GSI を作ってみます。

GSI の作成 ①

GSI はテーブル作成後にも追加できるため、作ってみます。

スクリーンショット 2025-11-30 5.25.26.png

最大でさらに 3 個の属性を追加できます。と記載あります。これが新機能みたいです!

スクリーンショット 2025-11-30 5.27.20.png

今回は「特定のゲームの、特定の日付のハイスコアランキングを取得したい」という用途で作ってみます。

GameName,ScoreDateをパーティションキーにし、HighScoreをソートキーにします。

image-6.png

これまでだとこれを実現しようとすると、GameName#ScoreDateみたいなキー結合が必要でした。

動作確認 ①

では動作確認してみます。

PuzzleQuest2025-01-01のデータを確認してみます。

image-8.png

無事取得できました。

なお、パーティションキーですが、複数属性をパーティションキーに指定した場合、クエリ時に全属性の指定が必要の模様です。

image-7.png

GSI の作成 ②

次は、複数ソートキーも試してみます。

「特定のゲームの全期間のスコア(日付順 → スコア順)」を取得するユースケースです。

まず、検証用にPuzzleQuestのデータを追加します。

UserId GameName HighScore ScoreDate
user3 PuzzleQuest 12000 2025-01-01
user4 PuzzleQuest 20000 2025-01-01
user3 PuzzleQuest 16000 2025-01-02
user4 PuzzleQuest 19000 2025-01-02

これでPuzzleQuestに対して、複数の日付、同じ日付で複数のスコアが存在する状態になります。

次に GSI を作成します。

GameNameをパーティションキーにし、ScoreDate,HighScoreをソートキーにします。

image-9.png

動作確認 ②

まずは、パーティションキーのみで表示してみます。

ソート順である、ScoreDate,HighScoreで並んでいますね。

image-10.png

次にソートキーScoreDateで絞っていきます。

ソートキーは順序が決まっており、今回の場合は最初の項目ScoreDateで絞り込むことができます。

2025-01-01分で絞れましたね。
取得件数、スキャン数ともに 2 件なので、問題なさそうです。

image-11.png

次にHighScoreで絞り込みます。

項目を追加すると、最初のソートキーScoreDateの条件が選択できなくなり、完全一致のみになりましたね。

これは複数ソートキーを使う場合、最後のソートキーのみ、複数の検索条件が利用可能になるという仕様によるものです。

image-12.png

なお、ソートキーの指定順序・検索条件の制限などの詳細は以下を参照してください。

まとめ

DynamoDB のマルチ属性複合キー対応により、GSI で複数属性を組み合わせた検索が可能になりました。

これにより、ホットパーティションの軽減や、アプリ側でのキー結合の手間が減り、設計がシンプルになるケースが増えると思います。

なお、使用時の注意点として以下があります。

  • 複数属性をパーティションキーに指定した場合、クエリ時に全属性の指定が必須です
  • 複数ソートキーを使用する場合、左から順に指定する必要があり、途中の属性をスキップできません
  • 範囲検索(>, <, BETWEENなど)は最後のソートキーのみ使用可能で、それ以前のソートキーは完全一致(=)のみ指定できます

DynamoDB の使い勝手が良くなって嬉しいなぁ。と感じるアップデートでした!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?