Python 中的 DynamoDB 批量写入(boto3 batch_writer)
batch_writer() 是唯一一个 Python 比其他 SDK 更省事的 DynamoDB 调用。它会缓冲 put 和 delete,把它们切成每批 25 条的 BatchWriteItem 请求,并自行重发未处理的项目。它不会替你挡住的,是那两个毁掉大多数批量导入的故障——而且这两个都在刷新缓冲区时才浮出水面,而不是在提供那条坏项目的那一行。
代码
import boto3
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("Music")
songs = [
{"Artist": "Arturo Sandoval", "SongTitle": "Cubano Chant", "AlbumTitle": "Danzon", "Year": 1994},
{"Artist": "Arturo Sandoval", "SongTitle": "A Mis Abuelos", "AlbumTitle": "Danzon", "Year": 1994},
{"Artist": "Arturo Sandoval", "SongTitle": "Groovin' High", "AlbumTitle": "Swingin'", "Year": 1996},
]
with table.batch_writer() as batch:
for song in songs:
batch.put_item(Item=song)
# batch_writer buffers deletes too — target a key you're NOT also putting
# (two writes to the same key in one batch are rejected as a duplicate)
batch.delete_item(Key={"Artist": "Ella Fitzgerald", "SongTitle": "Misty"})
print(f"Buffered {len(songs)} puts + 1 delete; the batch flushes on exit")说明
- 延迟刷新——
batch.put_item()只是往一个列表里追加。在缓冲区攒够 25 条或with块退出之前,什么都不会被校验、序列化或发送,所以坏项目的堆栈跟踪来自刷新,而不是来自提供它的那次put_item调用。如果你是从迭代器加载数据的,请自己记录进入缓冲区的索引。 - 纯 Python 值——这是 resource API,所以你写
1994,而不是{"N": "1994"}。任何带小数的值都必须用 Decimal;float会被接受进缓冲区,然后在刷新时被拒绝。 batch_writer()是Table上的方法。读取侧的对应物却不是:batch_get_item挂在ServiceResource上,而table.batch_get_item并不存在。批量读取根本没有任何缓冲、切分或重试助手。- 是
UnprocessedItems,不是错误——那是它唯一处理的重试。被限流的写入会被重发;ValidationException则会向上抛出。改走client.batch_write_item就等于把整个循环交还给你,就像 Node.js 示例里那样。 - 它无法抬高服务限制。每个请求 25 次写入、每个项目 400 KB、每个请求 16 MB,不支持条件也不支持更新,而且每次 put 都会整体替换已存储的项目。需要一个防护条件,或者要全有全无?请看 TransactWriteItems。
batch_writer 在刷新时到底做了什么
缓冲 30 次 put,看看它发出了哪些调用。包装 table.meta.client.batch_write_item 并记录请求大小,针对 DynamoDB Local 3.3.0:
batch sizes sent: [25, 5]两个请求,按服务限制切分,余数由 __exit__ 刷出。这个刷新是无条件的:在块里抛出 RuntimeError,缓冲的项目照样会在退出时写入。半路挂掉的批量导入留下的是一份写了一半的数据,而不是一张白纸。
现在看那两个故障。缓冲同一个键两次,只要源数据里出现重复,这就会发生:
with table.batch_writer() as batch:
batch.put_item(Item={"Artist": "Dup", "SongTitle": "Key", "Year": 1})
batch.put_item(Item={"Artist": "Dup", "SongTitle": "Key", "Year": 2})botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the
BatchWriteItem operation: Provided list of item keys contains duplicates两次 put_item 都没有吭声。除非你明确要求,batch_writer() 不会去重,而要求的方式是 table.batch_writer(overwrite_by_pkeys=["Artist", "SongTitle"])。把同样这两次 put 走一遍,项目会存成 Year: 2——缓冲区对每个键只保留最后一次写入,所以如果你那两行本该是键取错了的两个不同项目,这次去重就是一次无声的数据丢失。
第二个故障是 boto3 独有的,根本到不了 DynamoDB:
TypeError: Float types are not supported. Use Decimal types instead.一个值为 4.5 的 Rating 会安静地待在缓冲区里,然后在刷新时炸开。Decimal("4.5") 能正确地往返为 {"N": "4.5"}。用 json.loads 从 JSON 里读一个价格或评分,每个数字都是 float,所以对多数导入脚本来说这是第一次运行就会遇到的失败。给 json.loads 传 parse_float=Decimal 可以从源头解决它。
如果你在手工地把原生 Python 值和线路格式来回转换,DynamoDB JSON 转换器会把同一个项目的两侧同时显示出来,好让你看清你的 Decimal 实际变成了什么。
要从 CSV 或 JSON 批量导入而不必自己写类型映射,请下载 DynoTable。
相关示例
- Node.js 中的 DynamoDB BatchWriteItem——batch_writer 藏起来的那个手写重试循环。
- 用 AWS CLI 做 DynamoDB BatchWriteItem——同样的批量写入,从命令行发出。
- Python 中的 DynamoDB PutItem——这里被批处理的单项写入。
- DynamoDB 中的批量操作——限制、部分失败,以及批处理什么时候划算。
- "Too many items requested for the BatchWriteItem call"——一批里超过 25 个 put/delete 请求。
- "Provided list of item keys contains duplicates"——一批里有两个请求命中了同一个键。
参考资料
- Amazon DynamoDB guide (batch_writer) — Boto3 documentation
- BatchWriteItem — Amazon DynamoDB API Reference
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
最后核实于 2026-07-28,依据上方链接的 AWS 官方文档。