DynamoDB 初心者が AWS Summit で「GSI マルチキーサポート」のセッションから学んだこと
こんにちは~ 浮田です。
■ TL;DR
- DynamoDB の GSI が、PK・SK に複数属性(各最大4つ)を指定できるようになった
- これまで必要だった「文字列の連結(複合キー)」と「バックフィル」が不要に
- ただし GSI 限定。クエリは「PK は全部 =、SK は左から順・範囲は最後だけ」のルールあり
■ 目次
- そもそも DynamoDB のキーって?
- GSI が必要になる場面
- 従来の複合キー GSI、3つのつらみ
- 新機能:GSI マルチキーサポート
- ここが肝:クエリのルール
- その他の注意点
- まとめ
AWS Summit のセッション「マルチキーサポートで変わる Amazon DynamoDB の GSI 戦略(CNS204)」を受けてきました。
登壇は アマゾン ウェブ サービス ジャパンの岩瀬さんです。
私は DynamoDB をほとんどさわったことがない初心者なのですが、「GSI」「マルチキー」と聞いて身構えていたのが、セッションを通して「なんだ、そういうことか」と腑に落ちました。同じく DynamoDB はこれから、という方に向けて、何が嬉しい新機能なのかを噛み砕いて書きます。
結論を先に言うと、この機能のおかげで「複数の条件で検索したいときに、自分で文字列をこねくり回す」という初心者泣かせの作業が要らなくなります。
■ そもそも DynamoDB のキーって?
本題に入る前に、セッション冒頭の「各種キーのおさらい」がとても良かったので、そこから。
DynamoDB は AWS の NoSQL データベースで、データを取り出すときは必ず「キー」を指定します。このキーは2種類の役割に分かれます。
- パーティションキー(PK)…「何を探すか」。データを特定するために必ず指定が必要で、データの物理的な置き場所を決める
- ソートキー(SK)…「どう絞り込むか」。範囲検索やソートに使い、同じ PK を持つデータの集合を扱える(1対多の表現)
ここを図書館でたとえると分かりやすいです。
図書館 = テーブル、本 = データ1件、本に書かれた情報(著者・ジャンルなど) = 属性。
そして司書が本を「ジャンルごとの棚」に置いていくように、DynamoDB もパーティションキーという属性を見て、データの置き場所を決めます。棚の中で本がタイトル順に並ぶのがソートキーです。
ところが DynamoDB は、キーにしていない情報で探すのがとても苦手です。「料理コーナー」に置いた本を、後から「この著者の本だけ集めて」と言われても、棚はジャンル順なので全部見て回るしかありません。
そこで登場するのが GSI(グローバルセカンダリインデックス)。ひとことで言うと「同じデータを別のキーでもう一度引けるようにした、もう一組の棚」です。
著者順に並べ直した棚を別に用意しておけば、「この著者の本を全部」も高速に引けるようになります。本体の棚(ベーステーブル)はそのままに、引き口だけ増やせるわけです。
■ GSI が必要になる場面
先ほども書きましたがDynamoDB は、キーにしていない項目での検索がとても苦手です。
これが GSI の出番につながります。
セッションのデモは IoT センサーデータが題材でした。
温度・湿度・振動などのセンサーが、受信のたびにデータを保存していくテーブルです。
|
項目 |
役割 |
|
reading_id |
テーブルの PK。受信ごとに毎回違う値が入る想定 |
|
device_id |
デバイス固有の ID |
|
sensor_type |
センサーの種別(temperature など) |
|
timestamp |
受信日時 |
|
reading_value |
センサー値 |
|
status |
センサーステータス(NORMAL / ALERT など) |
ここで「あるデバイスの、ある種類のセンサーの値を調べたい」となると、検索したいのは device_id や sensor_type です。でもこれらはキー(reading_id)ではありません。
PK 以外の項目で検索すると、すべて Filter 指定になり全件処理になるという点でした。
Filter は「いったん全部読み込んでから、後で条件に合わないものを捨てる」という動きをします。
つまり欲しい数件のために、テーブル全体を読んでいることになります。データが増えるほど、これは効いてきます。
そこで、別のキーで引けるようにする GSI(グローバルセカンダリインデックス)を用意します。
GSI は、ざっくり言えば「同じデータを別のキーで引けるようにした、もう一組の棚」です。
■ 従来の複合キー GSI、3つのつらみ
ところが、従来の GSI には初心者がつまずくポイントがありました。岩瀬さんはこれを「従来の複合キー GSI の課題」としてデモで見せてくれました。
● つらみ1:GSI は PK・SK ともに1属性しか指定できなかった
「デバイスとセンサーを指定して、ある時間のセンサー値を検索したい」。やりたいことは device_id と sensor_type の2つを組み合わせた検索です。
でも従来の GSI は、PK も SK もそれぞれ1属性しか指定できませんでした。2つの値を1つのキーにしたい。
● つらみ2:文字列を連結した「複合キー」が必要
1属性しか使えないので、複数の値を自分でつなげて1つのキーにします。いわゆる複合キー(synthetic key)です。デモのコードはこんな雰囲気でした。
# 読み・書きのたびに連結ロジックが必要
composite_key = f"{device_id}#{sensor_type}"
item = {
"reading_id": "RD-001",
"device_id": "SENSOR-001",
"sensor_type": "temperature",
"deviceSensor": composite_key, # ← GSI のためだけの冗長な項目 ("SENSOR-001#temperature")
# ...
}
岩瀬さんが強調していたのは、deviceSensor という項目が実在するデータとは別の、GSI のためだけに作った値だという点です。本来のデータモデルとズレた項目が増えるので、データを見ても意図が読み取りにくく、アプリのロジックにも連結処理が散らばって可読性が落ちます。
● つらみ3:バックフィルが必要
既存のテーブルに、後からこの複合キー GSI を足すとどうなるか。すでに入っている全データに対して、SENSOR-001#temperature のような連結値を作って埋めていく必要があります。これがバックフィルです。
デモでは、全件をループで処理していく図が示されていました。ポイントは、この処理がデータ量に比例することです。データが100万件あれば、100万件ぶんを読み込んで・連結値を作って・書き戻すことになり、件数がそのまま処理時間として影響が出ます。
データ量が多いテーブルほど、この処理時間もバックフィルとしてのしかかります。さらに、連結に使う項目のどれかが欠けると正しくインデックスされない、という落とし穴もあります。
■ 新機能:GSI マルチキーサポート
ここからが本題です。今回のマルチキーサポートで、GSI の PK・SK にそれぞれ最大4属性、合計最大8属性をそのまま指定できるようになりました。
さきほどの例なら、複合キーを作らずに、こう書けます。
GSI PK = device_id, sensor_type
GSI SK = timestamp, reading_value
device_id と sensor_type を、別々の属性のまま PK にできる。連結も、deviceSensor のような冗長な項目も要りません。
従来との違いを、セッションの比較表をもとにまとめるとこうです。
|
項目 |
従来(文字列結合) |
新機能(マルチ属性) |
|
GSI PK |
1属性 |
最大4属性 |
|
GSI SK |
1属性 |
最大4属性 |
|
複合キー属性 |
必要(冗長) |
不要 |
|
データ型 |
すべて文字列 |
各属性のネイティブ型を保持 |
|
バックフィル |
新パターン追加時に必要 |
不要 |
個人的に効いたのが「データ型」の行です。複合キーは文字列に押し込むので、数値も文字列になってしまいます。マルチキーなら reading_value は数値のまま保てるので、後で出てくる数値の範囲検索が素直にできます。
● 後から GSI を追加するときの差も大きい
既存テーブルに新しい検索パターンを足すときの手順も、はっきり減ります。
従来は「新しい複合キー属性を設計 → アプリに連結ロジックを追加 → 既存データ全件にバックフィル → GSI を作成 → クエリコードを更新」の5ステップ。
マルチキーなら「既存属性をそのまま指定して GSI 作成 → クエリコードを更新」の2ステップで、バックフィルの処理を新たに実装する必要がありません。新しいアクセスパターンへ柔軟に対応できる、というのが大きな利点でした。
■ クエリのルール
便利になった一方で、クエリの書き方にはルールがあります。セッションでも丁寧に 深掘りしていた部分でした。
クエリ指定のルールは3つ。
- Rule 1:PK の全属性に =(等価)条件が必須。一部だけ・不等号では引けない
- Rule 2:SK は左から順に指定(先頭や中間だけのスキップは不可。全部指定はOK)
- Rule 3:SK は範囲検索も可能
そして「SK のルールと設計」のスライドが実践的でした。
- 等価条件(=)で使う属性を先頭に(例:timestamp で特定時刻を指定 → SK の先頭に置く)
- 範囲検索(>=, BETWEEN など)したい属性は最後に(例:reading_value で閾値超えを検索 → SK の末尾に置く)
たとえば GSI SK = timestamp, reading_value と組んでおけば、こんな検索ができます。
device_id = "SENSOR-001"
AND sensor_type = "temperature" # PK は全部 = で指定(Rule 1)
AND timestamp = "2025-01-15T10:00" # SK 先頭は等価(設計1)
AND reading_value >= 40 # SK 末尾は範囲(設計2 / Rule 3)
「特定デバイスの温度センサーで、この時刻に、閾値40以上の値」を一発で引ける。文字列連結のトリックなしで、素直に読めるのが気持ちいいです。
「アクセスパターン洗い出しが重要となる」。
どんな並び順でキーを組むかは、どう検索したいかで決まる。DynamoDB は検索条件を後から自由に足せる RDB とは違い、引き方を先に決めて設計するのが基本だ、と設計する際の指針を得ました。
■ その他の注意点
細かいですが、初心者がハマりそうな点も拾われていました。
- サポートされる型は String (S) / Number (N) / Binary (B) のみ
- 既存の GSI への影響はない(必要に応じて変更すればよい)
- GSI に設定した項目のいずれかの値が欠落していると、その項目はインデックス化されない
特に最後の「欠落するとインデックスされない」は、データの入れ方次第で「検索したのに出てこない」が起きうるので、覚えておきたいところです。
■ まとめ
● この機能で変わること
- 文字列結合・バックフィル・型喪失が解消される
- 開発体験・保守性・ストレージ効率が向上する
- 効率化でキャパシティユニットの利用が減り、コスト面でも有効
● 最適な利用場面
- 複数属性を組み合わせた検索をしたいとき
- スキーマ・コード・ログの可読性を高めたいとき
- 型安全性を保ちたいとき
DynamoDB 初心者として一番の収穫は、「キーは1つずつ」という制約が外れて、複数の軸での絞り込みが素直に書けるようになったという一点に尽きます。
これまで複合キーで頑張っていた部分が自然な属性のまま扱えるようになり、初心者がつまずきやすい「キー設計の難しさ」のハードルがひとつ下がった実感があります。
ただし GSI 限定であること(ベーステーブルのプライマリキーは従来どおり)、クエリには順番と範囲のルールがあることは押さえておく必要があります。
これから DynamoDB を始める人ほど、最初からこの新機能を前提に設計を学べるのは恵まれているなと感じました。