DynamoDBのGSI設計について

Blog

DynamoDBのGSIの性質について学んだことを備忘録として書いてみます。

やりたかったこと

前提として、やりたかったことは必須ではないキーを含めた複合キー(例:ParentID#子ID)を使用してインデックス(GSI)が組みたいという場面でした。

また、検索したい要件としては全件検索と、例でいうParentIDでの絞り込みが必要でした。

最初に考えた案としては、以下の2つです。

  1. 値がない場合、その値を除いた複合キーで登録
  2. NONEなど固定値を入れる

結論から言うと、今回のアクセスパターン(全件一覧 + ParentID絞り)であれば**インデックスはそのままで、ParentID属性があるものだけをGSIで使う(=親ありだけがGSIに載る)**設計で十分でした。

詳しく解説します。

GSIの性質

前提として、GSIの性質をおさらいします。

✅ GSIは「インデックスキー属性が存在しないアイテムはそのインデックスに登録されない」

GSIのパーティションキーまたはソートキーとして定義された属性がアイテムに存在しない場合、そのアイテムはそのGSIには載らない(=クエリ対象にならない)ということです。

つまり先述した1の対応(値がない場合、その値を除いた複合キーで登録)をした場合、そもそもインデックスに乗っていないので検索できないということになります。

ここが知識として抜けていました。

✅ GSI設計は「何をどう問い合わせたいか(アクセスパターン)」から逆算する必要がある

NoSQL設計の基本として、どのような問い合わせ(クエリ)を行いたいかを起点にキー設計(PK,SK,GSI)を決めるべきとされています。

案1. 「値がない場合、その値を除いた複合キーで登録」がNGな理由

この案では、例えば想定していた複合キー(例:ParentID#子ID)から「子IDがない」場合にParentIDだけをキーにする、というような実装で子IDがないアイテムも扱えるようにする想定でした。

ですがこれだと「親+子あり」「親のみ」「子のみ」のように複数のケースを扱わないといけなくなり、ロジックが複雑化してしまいます。

単一テーブル設計で多様なアクセスパターンを1つのキーで表現したい場合は有効なようですが、今回はそうではなかったため適切ではありません。

案2. 「NONEなど固定値を入れる」がNGな理由

こちらは、キー属性が“値がない”場合でも、代わりに特定の固定文字列(例:"NONE")を格納して、GSIのキー属性を常に存在させる方式です。

案1で問題だった「GSIに乗らない」という点はクリアすることができますが、親(ParentID)で検索をする際にNONEで登録されているアイテムも取得されてしまいます。

もし親ありだけでなく親なしの一覧も取得したい場合は、別のインデックスを作成(GSIを追加)で対応するのが良いです。

まとめ

“アクセスパターンごとにキー/インデックスを設計する”というNoSQL・DynamoDB設計原則の重要性がよく分かりました。

NoSQL設計もっと慣れていきたいです。

参考:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-general-nosql-design.html