DynamoDBはクエリーフィルターではなくインデックスを利用しましょう!設計テクニックを紹介します🐕

DynamoDBはクエリーフィルターではなくインデックスを利用しましょう!設計テクニックを紹介します🐕

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

想定する読者

  • DynamoDB で開発を行っている人
  • DynamoDB の設計を行っている人

はじめに

DynamoDB で、インデックス以外の属性情報で検索を行う際に、クエリーフィルターを使用すると思います。

事前にアクセス要件が定まっていなかったので、とりあえずインデックスつけていなかった…ようなケースは多いでしょう。

クエリーフィルターはインデックスよりも検索効率が悪い

クエリーフィルターは、取得後にフィルターを行うので、インデックスで検索するケースと比較してクエリー効率は悪いと言えます

低いデータ量のテーブルであれば問題ないですが、ボリュームが増すにつれてフェッチするデータが多くなるので、高スループットが必要となります。

クエリー API で検索条件がインデックスがしっかり指定されていれば、検索範囲を絞り込めるので効率が良いです。

基本的には、フィルターではなくインデックスを活用して設計を行うようにしましょう。

Composite Key (キー結合)

属性情報を結合してキー情報を作成します。

例えば、ゲームをプレイしたユーザーを記録するテーブル情報があるとします。

User Play Games Table

UserId(PK)Date(SK)GameIdStatus
竈門2020-05-10game-1DONE
富岡2020-05-20game-2IN_PROGRESS
鎧塚2020-05-19game-1HOLD

竈門くんのある期間でプレイしたゲームは検索できますが、「竈門くんが過去に終了したゲームは何か?」という検索に対応できません。つまり、現在のプレイ状況をキーに検索が行えません。

ここで使用するのが、Composite Key テクニックです。

UserId(PK)StatusDate(SK)GameId
竈門DONE_2020-05-10game-1
富岡IN_PROGRESS_2020-05-20game-2
鎧塚HOLD_2020-05-19game-1

Status と Date 属性を結合しソートキーを作成しクエリー API に指定します。(Begin_With のような指定が可能)こうすることで、クエリーフィルターよりも遥かにコストが安く検索が行えます。

ただこの場合、逆に「竈門くんのある期間でプレイしたゲームは何か」を検索したい場合にインデックスが効かなくなるので、それも踏まえると Date と StatusDate は両方持っていた方が安全かもしれません。(ステータスを指定せず日付だけでの検索が行えなくなります…)

Sparse indexes

NULL または undefined などの値を持つレコードをインデックスしない手法です。これを応用すると、あるゲームでユーザーランキング(1位〜3位)を算出し、データベースへ保持するときなどに非常に効率的なクエリーを実現可能です。

User Play Game Table

User(PK)Game(SK)ScoreDateGameRank
竈門Game-110002020-05-20Game-1_No2
富岡Game-112002020-05-19Game-1_No1
鱗滝Game-19002020-05-23Game-1_No3
鬼舞辻Game-18002020-05-10

User Game Rank GSI

GameRank(PK)UserDateScore
Game-1_No2竈門2020-05-201000
Game-1_No1富岡2020-05-191200
Game-1_No3鱗滝2020-05-23900

鬼舞辻くんの GameRank は NULL なので、GSI に登場しません。つまり、レコード量を小さく保つことが可能なので、クエリー実行時に効率よく検索が行えます。

GSI Overloading (GSIの多重定義)

  • GSI のテーブルあたりの作成数上限に関する制限を回避
  • 検索要件(インデックス要件)が定まっていない場合に大変強い

User Table

UserId(PK)NamePositionGenderDate
user-1竈門男性2020-05-10
user-2富岡男性2020-04-10
user-3栗花落女性2020-05-01
user-4村田男性2020-05-10

このテーブルに対して検索要件が定まっておらず、要件定義が難航した場合は下記のようにします。

Key(PK)Value(SK)UserId
氏名竈門user-1
性別男性user-1
役職user-1
氏名栗花落user-2

ユーザー一覧での検索要件が発生した場合でも、速やかに柔軟に対応することが可能です。例えばユーザーを氏名で検索したければ、key=氏名、Value={値}としてクエリー発行し対応可能です。

ただし、この方法はテーブル構造的に美しくない(個人的に)ので、乱用するのは推奨できません。第三者が見たときに美しいと感じるテーブル設計とはちょっと言えない気がします。(もちろんユースケース次第で採用しています)

まとめ

DynamoDB では、データアクセスを想定した様々なキー設計方法があります。デザインパターンを把握しておいて、引き出しにするのが良いでしょう。

サーバーレス開発については、お気軽にお問い合わせください。