DynamoDBの項目操作・Query・Scan・バッチ・トランザクション

DynamoDBの基本的な項目操作、QueryとScanの違い、ページング、バッチAPIの部分失敗、トランザクションのACID特性と使い分けを解説します。

学習順Step 94 / 101サービスデータベース試験ドメイン開発

DynamoDBの項目操作・Query・Scan・バッチ・トランザクション

Amazon DynamoDBでは、テーブル内のアイテムを主キーで読み書きするAPI、条件に合うアイテム群を取得するAPI、複数の操作をまとめて送るAPIを使い分けます。APIの選択は、アクセスパターンだけでなく、部分的な失敗を許容できるか、複数アイテムの整合性を一括で保証する必要があるかにも関わります。

1件のアイテムを操作するAPI

代表的な項目操作には、PutItem、GetItem、UpdateItem、DeleteItem があります。

API 主な用途 対象の指定
PutItem アイテムを新規登録する、または同じ主キーのアイテムを置き換える 登録するアイテムの中に主キー属性を含める
GetItem 主キーが一致する1件のアイテムを取得する Key にパーティションキーと、必要ならソートキーを指定する
UpdateItem 既存アイテムの属性を追加・変更・削除する Key で対象アイテムを指定する
DeleteItem 1件のアイテムを削除する Key で対象アイテムを指定する

PutItem は GetItem や UpdateItem のように独立した Key パラメーターで対象を渡す形式ではありません。しかし「キーの指定が不要」という意味ではありません。登録するアイテムには、テーブルのパーティションキーと、テーブルが複合主キーを使う場合はソートキーの属性と値を含めます。キー属性がなければアイテムを特定できず、書き込みは失敗します。

また、PutItem は既存アイテムへの属性追加専用ではありません。同じ主キーのアイテムがすでにある場合、通常は新しいアイテム内容で置き換えます。既存アイテムを意図せず上書きしたくない場合は、条件式を使ってアイテムが存在しないことを要求します。更新対象の属性だけを変更したい場合は UpdateItem を使います。

UpdateItem は対象アイテムがまだ存在しないときに呼び出されると、新しいアイテムを作ることがあります。既存アイテムに対する更新だけを許可したい場合は、条件式で存在を確認します。DeleteItem も必要に応じて条件を指定し、状態が期待どおりのアイテムだけを削除できます。

たとえば、配送アプリケーションで荷物の現在状態を管理する場合、packageId を主キーにして GetItem で状態を読み、UpdateItem で配達状況を更新できます。更新前の状態が「配達中」の場合だけ完了状態に変えるなら、条件式で現在値も検査し、古い状態を前提とした更新を防ぎます。

QueryとScanの違い

Query と Scan はどちらも複数のアイテムを読み取りますが、DynamoDBに読み取り対象を絞る方法が異なります。

比較 Query Scan
読み取り対象 テーブルまたはインデックスのパーティションキー値で特定される項目群 テーブルまたはインデックス内の全アイテム
キー条件 パーティションキー値の等価条件が必要。ソートキー条件も追加できる 特定のキー範囲を選ぶためのキー条件はない
主な用途 決まったアクセスパターンに沿って対象アイテムを検索する 全件確認や、少数回の探索・保守処理
読み取り負荷 キー条件で対象を絞れる 件数やアイテムサイズに応じて全体を読み取るため大きくなりやすい

DynamoDBのQueryとScanが読み取り対象を決める方法

Query は、テーブルまたはインデックスのパーティションキー値を指定して、そのキーに属するアイテムを読みます。複合キーを使っている場合は、ソートキーに等価、範囲、前方一致などの条件を追加できます。たとえば、depotId をパーティションキー、arrivedAt をソートキーにした配送履歴なら、ある拠点のアイテムに絞ったうえで、時刻範囲を指定して問い合わせられます。

Query のキー条件で候補を選んだあとに、FilterExpression で追加の属性条件を適用することもできます。ただし、フィルターは読み取り後の結果に適用されます。フィルターで返却項目を少なくしても、読み取ったデータ量や読み取りキャパシティ消費は減りません。データアクセスを効率化するには、必要な条件をキー設計やインデックスで表現できるかを検討します。

Scan はテーブル、または指定したインデックスのアイテムを走査します。フィルターを付けても、読み取り後に返却結果が絞り込まれるだけです。テーブル全体から特定の属性を持つレコードを頻繁に探す設計を Scan に任せると、データ量の増加に合わせて余分な読み取りや待ち時間が増えます。反復的な検索には、検索条件を満たす Query とキー・インデックスの設計を優先します。

インデックスに対する Scan も、そのインデックス内の全アイテムを走査する操作です。インデックスを使っただけでキー検索になるわけではありません。また、ProjectionExpression で返す属性を減らしても、読み取り容量が自動的に削減されるわけではありません。

1 MBの応答上限とページング

Query と Scan は、1回のAPI呼び出しで処理・返却するデータ量が最大1 MBです。条件に一致するアイテムがそれ以上ある場合は、結果が複数ページに分かれます。アプリケーションは必要に応じて続きのページを読み込みます。

応答に LastEvaluatedKey が含まれていれば、その位置以降に読み取るアイテムが残っていることを示します。次のリクエストでは、その値を ExclusiveStartKey に設定します。ページを読み進め、LastEvaluatedKey が返らなくなった時点で結果の末尾に到達しています。

DynamoDBのQueryとScanでLastEvaluatedKeyを使って結果をページングする流れ

1 MBは、クエリ全体の結果サイズ上限ではなく、1回の読み取り応答に関する上限です。SDKのページネーターなどを使えば複数リクエストを順に処理できますが、全件を一度にメモリーへ集める必要があるとは限りません。画面表示や後続処理に必要な量に合わせて、ページごとに扱うことも検討します。

Limit を指定したときや FilterExpression を使ったときは、1 MBまたは指定件数を評価した後に返却数が決まります。そのため、ページの Items が空でも LastEvaluatedKey が含まれる場合があります。結果が空であることだけを見て検索終了と判断せず、継続キーの有無を確認してください。

バッチAPIで複数アイテムをまとめる

複数のアイテムを個別APIで1件ずつ処理すると、クライアントからのリクエスト数が増えます。DynamoDBのバッチAPIを使うと、複数の読み取りまたは書き込みを一度のリクエストにまとめ、項目ごとの通信を減らせます。

API まとめられる操作 代表的な上限 部分失敗時
BatchGetItem 複数キーの読み取り 1回あたり最大100項目、応答データは合計16 MBまで 未処理キーが UnprocessedKeys に返る
BatchWriteItem 複数アイテムのPutまたはDelete 1回あたり最大25操作、リクエストデータは合計16 MBまで 未処理操作が UnprocessedItems に返る

DynamoDBのバッチAPIが項目単位の未処理結果を返し、クライアントが再試行する流れ

BatchGetItem は複数キーの読み取りを並行して処理します。応答の並び順はリクエストのキーの並び順と一致するとは限らないため、返されたアイテムの主キーで対応関係を確認します。要求した全アイテムが一度に返るとは限らず、容量制限や内部処理上の理由で読み取れなかったキーは UnprocessedKeys に含まれます。

バッチAPIは複数の項目操作をまとめ、DynamoDB側で並行して処理します。入力した順番で実行される保証はありません。ある項目の処理結果を使って次の項目を決めるような順序依存の処理には使わず、項目単位で独立して扱える処理に利用します。

BatchWriteItem は複数の PutRequest と DeleteRequest をまとめます。既存アイテムの一部属性だけを変更する UpdateItem は含められず、条件式を使った個々の書き込みもできません。更新や条件付き操作が必要な項目は、個別APIまたはトランザクションAPIを使います。

BatchWriteItem に含めた Put または Delete の各操作は個別には原子的ですが、バッチリクエスト全体は原子的ではありません。一部の項目を処理できなくても、処理可能な項目は実行され、未処理分が応答に残ります。アプリケーションは未処理項目だけを再送し、すべて完了したかを追跡する必要があります。過剰な負荷のまま即時再試行を繰り返すと状況を悪化させるため、指数バックオフなどを用いて間隔を広げます。

この性質から、バッチAPIは「複数項目をまとめて効率よく送る」ために使い、「すべてが必ず同時に成功する」ことを期待してはいけません。途中で一部だけ書き込まれても許容でき、未処理分を安全に再試行できる処理に向いています。

トランザクションで複数アイテムの整合性を保つ

複数アイテムをまとめて読み取る TransactGetItems と、複数アイテムをまとめて変更する TransactWriteItems は、トランザクションとして一括実行されます。トランザクション内のすべてのアクションが成功するか、失敗時にはどの変更も適用されないというACID特性を提供します。

TransactWriteItems は、Put、Update、Delete、ConditionCheck を組み合わせて使えます。TransactGetItems は複数アイテムの読み取りに使います。これらの操作は複数のテーブルにまたがる場合もありますが、同じAWSアカウントとリージョン内のテーブルが対象です。1回のトランザクションには最大100件のアクションを指定でき、すべての項目データの合計サイズは4 MBまでです。同じアイテムに対して同一トランザクション内で複数アクションを重ねることはできません。

DynamoDBトランザクションで条件確認と複数アイテムの更新をまとめる流れ

たとえば、オンライン講座の受講登録で定員の二重消費を防ぐ場面を考えます。残席数を減らす更新に「残席が1以上」という条件を付け、受講者の登録アイテムを追加し、登録確認用のアイテムも更新するとします。これらを個別のAPIで順に実行すると、途中で失敗したときに一部だけが反映され、残席数と登録データが食い違うことがあります。関連する変更を一つのトランザクションにまとめれば、条件を満たす場合にすべて反映し、定員条件に違反したり別の処理と競合したりした場合は全体を取り消せます。

トランザクション内で条件を確認すると、複数のアイテムに関係する不変条件を守れます。APIが成功しなかった場合は、条件不成立、他処理との競合、容量不足などの理由を確認します。競合が起きる処理では、必要に応じて安全な再試行を設計します。トランザクションは複数アイテムの整合性を提供する一方、通常の単一アイテム操作やバッチより多くの処理容量やコストを要する場合があるため、必要な整合性の境界を定めて利用します。

バッチとトランザクションを選ぶ

バッチとトランザクションは、どちらも複数項目を一つのAPIリクエストに含められます。しかし、失敗時の扱いが異なるため、要件から使い分けます。

判断軸 バッチAPI トランザクションAPI
主な目的 多数の項目をまとめて読み書きし、通信を効率化する 複数アイテム間の整合性を保ちながら一括操作する
成功の単位 項目ごと。部分的に成功することがある トランザクション全体。すべて成功するか全体が失敗する
条件付き操作 BatchWriteItem では不可 条件確認と書き込みをまとめられる
未処理・失敗への対応 未処理項目を取り出して再試行する 失敗理由を確認し、業務条件に応じて全体を再試行するか処理を中止する
選択例 初期データの一括登録、互いに独立した複数アイテムの取得 在庫・予約・登録など、複数アイテムの状態を同時に保つ必要がある処理

大量処理で項目間の依存がなく、一部失敗を検知して再試行できるならバッチAPIが候補です。一つのアイテムの変更を別アイテムの変更と不可分にしなければ業務上の不整合が起きるなら、トランザクションAPIを選びます。

トランザクションを使わず、バッチ書き込みを順番に実行するだけでは、関連アイテム間の一貫性は保証されません。逆に、項目が互いに独立している処理をすべてトランザクション化すると、必要以上の処理容量とコストを使う可能性があります。必要な整合性の範囲、条件判定、失敗後の回復方法を先に明確にします。

API選択の確認手順

確認事項 選択の考え方
対象が1件か、複数件か 1件なら GetItem / PutItem / UpdateItem / DeleteItem を基本にし、独立した複数項目ならバッチも検討する
検索条件がキーで表現できるか パーティションキー値と必要なソートキー条件を指定できるなら Query を使う。全体の列挙が必要な場合は Scan を使う
返却項目が1 MBを超える可能性があるか LastEvaluatedKey と ExclusiveStartKey を使い、ページングを実装する
フィルター後の件数だけで考えていないか フィルター適用前に読んだデータ量も考慮し、繰り返す検索ならキー設計やインデックスを見直す
バッチ内の一部失敗を許容できるか 許容できるなら未処理項目の追跡と再試行を実装する
複数項目を一括で成功または失敗させる必要があるか 整合性が必須なら条件付きアクションを含むトランザクションを使う

AWS公式ドキュメント

関連トピック

重要ポイント

  • ▸PutItemは新しいアイテムを登録するAPIだが、アイテム内にはテーブルの主キー属性が必要。同じキーの既存アイテムは、条件を付けなければ置き換わる
  • ▸GetItem、UpdateItem、DeleteItemは主キーを指定して対象を特定する。UpdateItemは存在しないアイテムを作成する場合もある
  • ▸Queryはパーティションキーの値を指定して対象を絞り、必要に応じてソートキー条件を追加する。Scanはテーブルまたはインデックスの全アイテムを走査する
  • ▸QueryとScanは1回の応答が最大1 MBでページングされる。LastEvaluatedKeyが返った場合はExclusiveStartKeyに渡して続きのページを取得する
  • ▸FilterExpressionは読み取り後に結果を絞るため、読み取るデータ量や読み取りキャパシティを減らさない。QueryでもScanでも注意が必要
  • ▸BatchGetItemは最大100項目、BatchWriteItemは最大25件のPut/Deleteをまとめるが、バッチ全体は原子的ではなく、未処理項目を再試行する必要がある
  • ▸TransactGetItemsとTransactWriteItemsは複数項目の処理をACIDトランザクションとして実行し、いずれかが失敗するとトランザクション全体が適用されない
  • ▸複数項目をまとめたいだけならバッチ、項目間の整合性を一括で守る必要があるならトランザクションを選ぶ

このトピックの学習を完了しますか?

完了状態はいつでも切り替えられます