DynamoDB で、インデックス以外の属性情報で検索を行う際に、クエリーフィルターを使用すると思います。
事前にアクセス要件が定まっていなかったので、とりあえずインデックスつけていなかった…ようなケースは多いでしょう。
クエリーフィルターは、取得後にフィルターを行うので、インデックスで検索するケースと比較してクエリー効率は悪いと言えます。
低いデータ量のテーブルであれば問題ないですが、ボリュームが増すにつれてフェッチするデータが多くなるので、高スループットが必要となります。
クエリー API で検索条件がインデックスがしっかり指定されていれば、検索範囲を絞り込めるので効率が良いです。
基本的には、フィルターではなくインデックスを活用して設計を行うようにしましょう。
属性情報を結合してキー情報を作成します。
例えば、ゲームをプレイしたユーザーを記録するテーブル情報があるとします。
User Play Games Table
UserId(PK) | Date(SK) | GameId | Status |
竈門 | 2020-05-10 | game-1 | DONE |
富岡 | 2020-05-20 | game-2 | IN_PROGRESS |
鎧塚 | 2020-05-19 | game-1 | HOLD |
竈門くんのある期間でプレイしたゲームは検索できますが、「竈門くんが過去に終了したゲームは何か?」という検索に対応できません。つまり、現在のプレイ状況をキーに検索が行えません。
ここで使用するのが、Composite Key テクニックです。
UserId(PK) | StatusDate(SK) | GameId |
竈門 | DONE_2020-05-10 | game-1 |
富岡 | IN_PROGRESS_2020-05-20 | game-2 |
鎧塚 | HOLD_2020-05-19 | game-1 |
Status と Date 属性を結合しソートキーを作成しクエリー API に指定します。(Begin_With のような指定が可能)こうすることで、クエリーフィルターよりも遥かにコストが安く検索が行えます。
ただこの場合、逆に「竈門くんのある期間でプレイしたゲームは何か」を検索したい場合にインデックスが効かなくなるので、それも踏まえると Date と StatusDate は両方持っていた方が安全かもしれません。(ステータスを指定せず日付だけでの検索が行えなくなります…)
NULL または undefined などの値を持つレコードをインデックスしない手法です。これを応用すると、あるゲームでユーザーランキング(1位〜3位)を算出し、データベースへ保持するときなどに非常に効率的なクエリーを実現可能です。
User Play Game Table
User(PK) | Game(SK) | Score | Date | GameRank |
竈門 | Game-1 | 1000 | 2020-05-20 | Game-1_No2 |
富岡 | Game-1 | 1200 | 2020-05-19 | Game-1_No1 |
鱗滝 | Game-1 | 900 | 2020-05-23 | Game-1_No3 |
鬼舞辻 | Game-1 | 800 | 2020-05-10 |
User Game Rank GSI
GameRank(PK) | User | Date | Score |
Game-1_No2 | 竈門 | 2020-05-20 | 1000 |
Game-1_No1 | 富岡 | 2020-05-19 | 1200 |
Game-1_No3 | 鱗滝 | 2020-05-23 | 900 |
鬼舞辻くんの GameRank は NULL なので、GSI に登場しません。つまり、レコード量を小さく保つことが可能なので、クエリー実行時に効率よく検索が行えます。
User Table
UserId(PK) | Name | Position | Gender | Date |
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 では、データアクセスを想定した様々なキー設計方法があります。デザインパターンを把握しておいて、引き出しにするのが良いでしょう。
サーバーレス開発については、お気軽にお問い合わせください。
スモールスタート開発支援、サーバーレス・NoSQLのことなら
ラーゲイトまでご相談ください
低コスト、サーバーレスの
モダナイズ開発をご検討なら
下請け対応可能
Sler企業様からの依頼も歓迎