中級読了 2 分

DynamoDB のキー条件式

キー条件式 は、Query に渡す KeyConditionExpression です — リクエストのうち DynamoDB がアイテムを 見つける ために使う唯一の部分です。それ以外のすべて(フィルタ、 プロジェクション)は、読み取りがすでに計測された後に動きます。

DynamoDB のキー条件式とは何ですか?

キー条件式とは、Query に渡す KeyConditionExpression のことで、DynamoDB がどのアイテムを読み取るかを指定します。は等価条件(PK = :v)でなければならず、には =<<=>>=BETWEEN、または begins_with のいずれかの範囲演算子を指定できます。フィルタとは異なり、何が読み取られ課金されるかを決めるのはキー条件式です。

  • パーティションキーは等価でなければならない。 PK = :v だけで、それ以外はなし — 範囲も begins_withIN もありません。DynamoDB はそれをハッシュ化して1つの パーティションを特定します。
  • ソートキーは範囲演算子を取る。 =<<=>>=BETWEEN、または begins_with — ここでアイテムコレクションをスライスします。
  • それはフィルタではない。 キー条件は何が 読まれ 課金されるかを決め、 FilterExpression は読み取り分を支払った後に結果を刈り込むだけです。
  • ソートキーはバイト順。 範囲演算子は辞書順で比較するため、ソートキー文字列をどう フォーマットするか あなたのクエリ力です。

なぜパーティションキーは等価に固定されているのか

DynamoDB はパーティションキーを物理パーティションにハッシュ化してアイテムを保存します。 ハッシュは範囲ではなく1つの位置をくれるため、またいで スキャンするものがありません。

だから PK > :vbegins_with(PK, :v) はきっぱり拒否されます。エンジンは「キーが X で 始まるすべてのパーティション」に、テーブル全体を読まずには答えられません — それはまさに それが避けるために作られた Scan です。

SQL から来ると、これは逆さまに感じます。WHERE id LIKE 'order%' は Postgres では些細 です。DynamoDB ではパーティションキーは検索可能な列ではなくアドレスです。

力が宿るのはソートキー

1つのパーティション内で、アイテムは ソートキーでソートされて 保存されます。その 順序付けこそ範囲演算子が活用するものです — DynamoDB はある位置にシークして前方に読みます。

演算子読むもの用途
SK = :v厳密な1アイテムキーで指定した特定の子
SK < / <= / > / >= :v開いた1スライス「この地点より後のすべて」
SK BETWEEN :a AND :b閉じた範囲(両端含む)境界付きの窓 — 日付範囲
begins_with(SK, :p)プレフィックスのスライスPK 下の種別や階層

キーには LIKECONTAINSENDS_WITH もありません。部分文字列と接尾辞のマッチは バイト順ではないため、全読み取りを強います — 設計上、API はそれを許しません。それらは すでに支払った FilterExpression に住みます。 (AWS: Key condition expressions

具体例:チャットアプリのメッセージ

チャンネルベースのチャットを作っているとします。1つのテーブルを、チャンネルで パーティショニングし、メッセージ時刻でソートします。オリジナルのキースキーマ:

  • パーティションキー ChannelRefCH#{channelId}
  • ソートキー PostedAt — ISO-8601 タイムスタンプ、MSG#2026-06-23T14:05:00Z

MSG# プレフィックスは、メッセージ行をソート可能に保ち、同じチャンネルの下に同居しうる 他の行種別(ピン留めした設定、メンバーシップ)と区別します。

チャンネルの最新メッセージを読み込む。 パーティションキーだけ、新しい順:

KeyConditionExpression:  ChannelRef = :ch
ExpressionAttributeValues:  { ":ch": "CH#general" }
ScanIndexForward:  false

ScanIndexForward: false はソート済みコレクションを逆向きに歩きます — クライアント側で ソートせずに「最新が先頭」を得る安価な方法です。

begins_with で特定の日。 タイムスタンプがソートキーでテキストとして保存されている ため、日付プレフィックスはきれいなスライスです。

KeyConditionExpression  ChannelRef = :ch AND begins_with(PostedAt, :day)
:ch    "CH#general"
:day   "MSG#2026-06-23"

これは 2026-06-23 のすべてのメッセージを、それだけを読みます — DynamoDB はプレフィックスに シークし、末尾から外れたら止まります。これが機能するのは、プレフィックスがバイトソートされた 文字列の真の左アンカーだからこそです。

BETWEEN で正確な窓。 「14:00 台のメッセージ」には、両端を含む範囲がプレフィックスに 勝ります。

KeyConditionExpression  ChannelRef = :ch AND PostedAt BETWEEN :lo AND :hi
:ch    "CH#general"
:lo    "MSG#2026-06-23T14:00:00Z"
:hi    "MSG#2026-06-23T14:59:59Z"

BETWEEN は両端を含むため、端点を意図的に選びましょう — ここでの off-by-one は、端の メッセージを静かに落とすか二重にします。

これらの式のいずれも、ExpressionAttributeValues マップが埋められた状態で、 DynamoDB Expression Builder で組み立ててコピー できます — begins_withBETWEEN の構文を一発で正しくするのに便利です。

DynoTable で見る

実際のチャンネルパーティションに対して同じキー条件を実行し、消費キャパシティがライブで 更新されるのを見てください。コレクション全体ではなくスライスを読んでいることを確認 できます。

罠:キー条件とフィルタの混同

高くつく間違いは、キーの仕事をさせるために FilterExpression に手を伸ばすことです。

KeyConditionExpression:  ChannelRef = :ch
FilterExpression:        begins_with(PostedAt, :day)

これは上の begins_with キー条件と 同等に見え、同じ行を返します — しかしまず チャンネルパーティション全体 を読み、それからその日の外のすべてを捨てます。全読み取り 分を課金されます。

フィルタは決して読み取りコストを削りません。DynamoDB がアイテムを計測した後に動きます — フィルタ付きの Scan と同じ落とし穴です。述語をキー条件に 入れられるなら、そこに属します。

修正は上流にあります。アクセスパターンを1つの PK 等価とソートキー範囲として表現できない なら、それはモデリングのサインです。ソートキーを作り直すか、そのパターン用にキーイング したインデックスを追加します — キーの並べ方は GSI と LSIシングルテーブル設計 を参照してください。

落とし穴と次のステップ

  • パーティションキーは常に = 範囲は決してなし。パーティションをまたぐ範囲が必要 なら、単一の Query を超えてしまっています。
  • クエリごとにソートキー条件は1つ。 2つのソートキー述語を AND できません。BETWEEN begins_with のどちらかを選び、両方は使えません。
  • 予約語にはエイリアスが必要。 TimestampName という名前のキーは ExpressionAttributeNames#ts)を使わないと、クエリがエラーになります。 (AWS: reserved words
  • BETWEEN は両端を含む。 両方の端点がマッチします — それに応じて境界を設計しましょう。

Expression Builder でキー条件を下書きし、それから DynoTable を試して 自分のテーブルに対して実行し、実際に消費するキャパシティを 見てください。

更新日