初級読了 3 分

DynamoDB の複合プライマリキー

複合プライマリキーは2つの属性です: パーティションキーソートキー。パーティションキーはアイテムがどこに存在するかを決め、ソートキーはそのパーティション内でアイテムを順序付けます。

SQL の出身者なら、これを一意の id 列としてではなく、GROUP BY partition, ORDER BY sort がテーブル自体に焼き込まれたものとして考えてください。

DynamoDB の複合プライマリキーとは?

DynamoDB の複合プライマリキーは、パーティションキーソートキーという2つの属性を組み合わせたものです。パーティションキーはアイテムがどの物理パーティションに存在するかを決め、ソートキーはそのパーティション内のアイテムを順序付けます。この2つが合わさってアイテムの一意な識別子を形成し、1回の Query で1つのアイテムではなくソート済みの範囲を返せるようにします。

  • 2つのパート、2つの仕事。 パーティションキーはアイテムを物理パーティションにルーティングし、ソートキーはそのパーティションキーを共有するすべてのアイテムを順序付けます。
  • 一意性はペア。 2つのアイテムは、ソートキーが異なる限り、パーティションキーの値を共有できます — それが1つのパーティションが多数の行を保持できる仕組みです。
  • ソートキーがすべての肝。 Scan なしに、1つのアイテムではなく範囲(>=betweenbegins_with)を Query が返せるようにするものです。
  • キーはスカラーでなければならない。 パーティションキーとソートキーは文字列、数値、バイナリのみで、マップもリストも不可です(AWS ドキュメント)。

単純なキー vs 複合キー

単純なプライマリキーはパーティションキーだけです。アイテムを一意に識別し、GetItem で読み戻します。それだけです — 範囲読み取りも、「最新の N 件を取得」もありません。

複合キーはソートキーを加えます。そのたった1つの追加が、DynamoDB をハッシュマップではなくデータベースのように感じさせるものです。

単純なキー複合キー
属性パーティションキーのみパーティションキー + ソートキー
一意性パーティションキーの値値のペア
パーティションあたり複数アイテム不可
範囲の Query不可(GetItem のみ)可(begins_withbetween>
自然な適合ID によるルックアップ時系列、1対多、履歴

センサー読み取り値のテーブルをモデル化する

フィールドセンサーの一群から温度サンプルを収集しているとします。アクセスパターンは「1つのデバイスの読み取り値を、新しい順に、時間枠内で取得する」です。これは教科書的な複合キーです。

デバイス ID をパーティションキーに、読み取り値のタイムスタンプをソートキーに使います:

deviceIdreadingTstempChumidity
DEV#a1b22026-06-23T08:00:00Z21.448
DEV#a1b22026-06-23T08:05:00Z21.747
DEV#a1b22026-06-23T08:10:00Z22.146
DEV#c9d82026-06-23T08:00:00Z19.855

3つの DEV#a1b2 の読み取り値はすべて同じパーティションに着地し、物理的にまとめて保存され、readingTs でソートされます。

AWS はパーティションキーをハッシュ属性、ソートキーを範囲属性と呼びます — ソートキーは、その中をスキャンできる範囲です(AWS ドキュメント)。

各パーティションキーの下でアイテムが1つのアイテムコレクションに集約される様子はこうです:

パーティション: DEV#a1b2readingTs 08:00readingTs 08:05readingTs 08:10Query deviceId = DEV#a1b2

パーティションキーに対する1回の Query が、そのデバイスのすべての読み取り値を、すでにタイムスタンプ順で読み取ります — クライアント側でのソートも、2回目の往復もありません。

範囲をスキャンせずに照会する

readingTs は ISO-8601 文字列なので、時系列順でソートするのと同じように辞書順でソートされます。だから時間枠の読み取りは、フィルタではなく、キー条件の範囲になります:

Query
deviceId  = "DEV#a1b2"
readingTs BETWEEN "2026-06-23T08:00:00Z" AND "2026-06-23T08:10:00Z"

これは KeyConditionExpression です — DynamoDB がデータを返すに読み取りを絞るので、枠内のアイテム分だけ料金を払います。FilterExpression は読み取りのに実行され、スキャンしたすべての分を請求します。それがミニチュア版の Scan の罠です。

その式そのものは、プレースホルダと型付けされた値があり、手で書くのは面倒です。DynamoDB Expression Builder で視覚的に構築し、正確な KeyConditionExpression を SDK 呼び出しにコピーしましょう。

ソートキーを意図的に設計する

ソートキーは無料のメタデータではありません — 範囲読み取りの唯一のレバーなので、クエリに合わせて形作りましょう。

  • ソート可能なタイムスタンプを使う。 ISO-8601 文字列やゼロ埋めしたエポック数値は正しくソートされます。生のローカライズされた日付はソートされません。
  • 1対多のオーバーロードのためにプレフィックスを付ける。 READING#2026-06-23T08:00:00Z のようなソートキーは、1つのパーティションの下にエンティティ型を混在させ、begins_with でスライスできるようにします。それがシングルテーブル設計への入り口です。
  • 高カーディナリティの次元をパーティションキーに置く。 センサー ID は数千の値を持つので、書き込みを均等に分散させます。低カーディナリティのパーティションキー(たとえば region)はホットパーティションを作ります。

複合キーが噛んでくるとき

これは便利さではなくコミットメントです。罠はこうです: パーティションキーを選んでリリースし、それから別のグループ化を必要とするアクセスパターンを発見します — 「フリート全体で30°Cを超えるすべての読み取り値」。

ベーステーブルはそれに答えられません。パーティションキーは固定されています。選択肢は、別のキーを持つグローバルセカンダリインデックスか、再構築です。

キースキーマをコミットするに、読み取りを列挙しましょう。プライマリキーの変更は ALTER TABLE ではなく、テーブル移行を意味します。

次のステップ

複合キーは、アイテムコレクション、1対多のリレーションシップ、そして最も有用なインデックス設計の基盤です — 次はシングルテーブル設計GSI と LSI の比較を読んで、それらがどこへ導くかを見てください。

DynamoDB Expression BuilderKeyConditionExpression を描き、DynoTable を試して、実際のパーティションを閲覧し、自分のテーブルに対してソート順が並ぶ様子を観察しましょう。

更新日