Python(boto3)中的 DynamoDB BatchGetItem
batch_get_item 一次请求按主键取回最多 100 个项目。下面代码块里的 while request_items: 就是 boto3 的全部惯用法:DynamoDB 会在一次_成功_的响应里把剩余项交还给你,而空字典是假值,所以循环会自己结束。限制和部分结果的规则见 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"]可以放心取下标,而那个假值空字典正是终止while的东西。要当心的是Responses:某张表的键全都没命中时,它压根不会出现在里面,这就是代码块用.get("Music", [])的原因。ConsistentRead和ProjectionExpression放在每张表自己的字典里,跟"Keys"并列,而不是跟RequestItems并列。boto3 会毫不犹豫地把放错位置的键发出去,然后让服务来拒绝它。- 这是低层 client,所以值是 DynamoDB JSON(
{"S": ...}、{"N": ...})。资源 API 把batch_get_item挂在 ServiceResource 上,而不是Table上。boto3.resource("dynamodb").batch_get_item(...)接受原生 Python 值;table.batch_get_item并不存在。这种不对称会让用过table.batch_writer()之后顺手去找它的人意外,因为后者_确实_是Table的方法。 - 退避只适用于
UnprocessedKeys。ValidationException是请求本身的 bug,重试它只是白白烧掉挂钟时间。
这次调用会抛出的两个错误,原样照录
两个都是客户端的错误,重试解决不了,而且都以一个普通的 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 duplicates第一个是 101 个键,第二个是同一个键被列了两次。注意你不能写成这样来捕获它们:
except client.exceptions.ValidationException: # AttributeErrorbotocore 在 DynamoDB client 上建模了 34 个命名异常类,而 ValidationException 不在其中。ConditionalCheckFailedException 和 ProvisionedThroughputExceededException 在,这就是条件写入那一页能按类捕获、而这一页不能的原因。甚至还有一个建模出来的 DuplicateItemException,而它并不是批次里键重复时你会拿到的东西。所以批量读取只能按错误码分支:
except ClientError as e:
if e.response["Error"]["Code"] == "ValidationException":
raise # a bug in the request; retrying will not help真正会在实际代码里咬人的是键重复那种情况,因为一份从 Query 结果或关联表拼出来的键列表天然会有重复。发送之前先去重,并记住两个字典只有在每一个键属性都相等时才算相等。
一个 100 项的批次之所以留不住 100 项,另一个原因是大小:每个项目计费时向上取整到 4 KB,而响应总量要算进 16 MB,所以 100 个 300 KB 的项目大约会回来 52 个,其余留在 UnprocessedKeys 里。项目大小计算器会给你用来相乘的单项目数字。
要先取回一组键、看看返回了什么再动手写循环,请下载 DynoTable。
相关示例
- Node.js 中的 DynamoDB BatchGetItem——用 AWS SDK v3 做同样的批量读取。
- 用 AWS CLI 执行 DynamoDB BatchGetItem——在 shell 里做同样的批量读取。
- Python 中的 DynamoDB GetItem——被批量化的那个单项目读取。
- DynamoDB 批量操作——限制、部分失败,以及批处理什么时候划算。
- "Too many items requested for the BatchGetItem call"——一次请求里超过 100 个键。
- "Provided list of item keys contains duplicates"——同一个键在一个批次里出现两次。
参考资料
- BatchGetItem — Amazon DynamoDB API Reference
- DynamoDB.Client.batch_get_item — Boto3 documentation
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
最后核实于 2026-07-28,依据上方链接的 AWS 官方文档。