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", []) 的原因。
  • ConsistentReadProjectionExpression 放在每张表自己的字典里,跟 "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 的方法。
  • 退避只适用于 UnprocessedKeysValidationException 是请求本身的 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:  # AttributeError

botocore 在 DynamoDB client 上建模了 34 个命名异常类,而 ValidationException 不在其中。ConditionalCheckFailedExceptionProvisionedThroughputExceededException 在,这就是条件写入那一页能按类捕获、而这一页不能的原因。甚至还有一个建模出来的 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

相关示例

参考资料

最后核实于 2026-07-28,依据上方链接的 AWS 官方文档。

无需控制台即可使用 DynamoDB

一款快速的 DynamoDB 桌面客户端,可运行 DynamoDB 无法执行的真正 SQL——JOINs、GROUP BY、聚合——并支持可视化编辑和运行在你自己的 Bedrock 密钥上的 AI agent。

30 天免费试用,无需信用卡 — 之后为无时间限制的免费版。