Python(boto3)での DynamoDB BatchGetItem
batch_get_item は 1 リクエストで プライマリキー指定のアイテムを最大 100 件 取得します。下のフェンスにある while request_items: が boto3 のイディオムそのものです。DynamoDB は 成功した レスポンスで残りを返してよこし、空の dict は偽値なので、ループは自分で終わります。上限と部分結果のルールは DynamoDB のバッチ操作にあります。このページで扱うのは boto3 の呼び出しと、それが送出するエラーです。
コード
import time
import boto3
client = boto3.client("dynamodb")
request_items = {
"Music": {
"Keys": [
{"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
{"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "A Mis Abuelos"}},
{"Artist": {"S": "Ella Fitzgerald"}, "SongTitle": {"S": "Misty"}},
]
}
}
items = []
attempt = 0
while request_items:
response = client.batch_get_item(RequestItems=request_items)
items.extend(response["Responses"].get("Music", []))
# A partial result is NOT an error: throttling, a >16 MB response, or an
# internal failure returns the leftovers in UnprocessedKeys. Retry them
# with exponential backoff.
request_items = response["UnprocessedKeys"]
if request_items:
attempt += 1
time.sleep(min(0.1 * 2**attempt, 5))
print(f"Fetched {len(items)} items")解説
response["UnprocessedKeys"]は常に存在します。完全に処理されたバッチでもキーは存在し、{}を保持します。だからrequest_items = response["UnprocessedKeys"]は安全に添字アクセスでき、偽値の空 dict がwhileを終わらせます。注意すべきなのはResponsesのほうです。キーがすべて外れたテーブルはそこに現れないので、フェンスでは.get("Music", [])を使っています。ConsistentReadとProjectionExpressionはテーブルごとの dict の中、RequestItemsの隣ではなく"Keys"と並べて置きます。boto3 は置き場所を誤ったキーも平気で送り、サービスに拒否させます。- これは低レベルクライアントなので、値は DynamoDB JSON です(
{"S": ...}、{"N": ...})。リソース API ではbatch_get_itemはTableではなく ServiceResource にあります。boto3.resource("dynamodb").batch_get_item(...)はネイティブな Python の値を取りますが、table.batch_get_itemは存在しません。この非対称は、table.batch_writer()を使ったあとに手を伸ばした人を驚かせます。あちらは 確かにTableのメソッドだからです。 - バックオフは
UnprocessedKeysにだけ適用します。ValidationExceptionはリクエスト側のバグであり、リトライは時計を空回りさせるだけです。
この呼び出しが送出する 2 つのエラー、逐語で
どちらもリトライでは直らないクライアント側の誤りで、どちらも素の botocore.exceptions.ClientError として現れます。DynamoDB Local 3.3.0 に対する str(e) はこうです。
An error occurred (ValidationException) when calling the BatchGetItem operation: Too many items requested for the BatchGetItem call
An error occurred (ValidationException) when calling the BatchGetItem operation: Provided list of item keys contains duplicates1 つ目は 101 個のキー、2 つ目は同じキーを 2 回並べた場合です。これらを捕まえるために 書けない ものにも注目してください。
except client.exceptions.ValidationException: # AttributeErrorbotocore は DynamoDB クライアント上に 34 個の名前付き例外クラスをモデル化していますが、ValidationException はその 1 つではありません。ConditionalCheckFailedException と ProvisionedThroughputExceededException は含まれます。だから条件付き書き込みのページではクラスで捕捉できるのに、こちらではできないのです。モデル化された DuplicateItemException すら存在しますが、バッチ内の重複キーで得られるのはそれでは ありません。というわけで、バッチ読み取りではコードで分岐するしかありません。
except ClientError as e:
if e.response["Error"]["Code"] == "ValidationException":
raise # a bug in the request; retrying will not help実コードで刺さるのは重複キーのケースです。Query の結果や結合テーブルから組み立てたキーのリストは、自然に重複するからです。送る前に重複を除きましょう。2 つの dict が等しいのはキー属性がすべて一致するときだけだ、という点をお忘れなく。
100 件のバッチが 100 件のままでいられないもう 1 つの理由がサイズです。各アイテムは課金上 4 KB に切り上げられ、レスポンスの 16 MB 上限に対して数えられるので、300 KB のアイテム 100 件は、およそ 52 件が返り、残りが UnprocessedKeys に入ります。アイテムサイズ計算ツールが、掛け算に使うアイテムあたりの数値を出してくれます。
キーの集合を引き戻し、ループを書く前に何が返ってきたか確かめるには、DynoTable をダウンロードしてください。
関連する例
- Node.js での DynamoDB BatchGetItem — AWS SDK v3 による同じバッチ読み取り。
- AWS CLI での DynamoDB BatchGetItem — シェルからの同じバッチ読み取り。
- Python での DynamoDB GetItem — これがバッチ化している単一アイテムの読み取り。
- DynamoDB のバッチ操作 — 上限、部分的な失敗、そしてバッチ化が報われる場面。
- "Too many items requested for the BatchGetItem call" — 1 リクエストに 100 を超えるキー。
- "Provided list of item keys contains duplicates" — 1 つのバッチに同じキーが 2 回。
参考資料
- BatchGetItem — Amazon DynamoDB API Reference
- DynamoDB.Client.batch_get_item — Boto3 documentation
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
最終検証日 2026-07-28、上記にリンクした公式 AWS ドキュメントに照らして確認しました。