进阶阅读约 3 分钟

DynamoDB 批处理操作:BatchGetItem 与 BatchWriteItem

当你需要一次读取或写入许多项时,为每个项各发一次 GetItemPutItem,就意味着每个项一次 网络往返 —— 既慢又啰嗦。DynamoDB 的批处理 API 把许多项操作折叠进一个请求:读取用 BatchGetItem,写入用 BatchWriteItem

它们是吞吐量与延迟上的胜利,而不是一致性保证 —— 而正是这个区分让人栽跟头。一个批处理不是 事务。

什么是 DynamoDB 批处理操作?

DynamoDB 批处理操作把许多项的读取或写入折叠进一个请求:BatchGetItem 至多取回 100 个项,BatchWriteItem 至多 put 或 delete 25 个项,二者各以 16 MB 为上限。它们省的是往返,而不是容量。关键在于,一个批处理不是事务 —— 各个项各自独立地成功或失败,没有回滚。

  • BatchGetItem —— 在一次调用里跨一张或多张表取回至多 100 个项(或 16 MB)。
  • BatchWriteItem —— 一次调用里至多 25 个 put/delete 操作(或 16 MB)。没有更新 —— 只有 put 和 delete。
  • 非原子。 个别项可能成功而其他项失败。没有回滚。
  • 部分失败是常态。 被限流的项会在 UnprocessedItems / UnprocessedKeys 里返回 —— 你必须自己带退避地重试它们。
  • 与逐个调用的容量成本相同 —— 批处理省的是往返,不是容量单元。

问题所在:许多项,一次往返

假设你运营一个客服台。一个仪表盘需要按 ID 加载 50 张工单来渲染一个队列;一个夜间任务归档 1,000 张已解决的工单。一次一个项地做,就是 50 次(或 1,000 次)顺序往返 —— 延迟层层叠加, 任务慢得爬行。

批处理把那些折叠成寥寥几次调用。读 50 张工单变成一次 BatchGetItem;归档任务变成一串 BatchWriteItem 调用,每次 25 个 delete。往返少得多,搬动的数据却一样。

批处理 API 如何工作

BatchGetItem 接受一组主键(跨一张或多张表)并返回匹配的项。你可以按表请求强一致读取。 凡是它读不到的 —— 通常是因为请求碰到了一个吞吐量上限 —— 会在 UnprocessedKeys 里返回, 而不是让整次调用失败。

BatchWriteItem 接受一个 PutRequest / DeleteRequest 操作的列表。注意缺了什么:没有 更新。一次批量写入要么替换整个项(put),要么移除它(delete)—— 要修改特定属性,你仍然需要 UpdateItem。它写不了的项会在 UnprocessedItems 里返回。

成功被限流退避后重试BatchWriteItem:25put/delete逐项处理已写入UnprocessedItems

关键的心智模型:一个批处理是一捆相互独立的操作,每个各自成功或失败 —— 不是一个全有或全无 的单元。

批处理不是事务

这就是陷阱。如果你归档任务的批处理在中途撞上了吞吐量上限,一些工单被删除了、一些没有 —— 而 DynamoDB 不会撤销那些已经过去的。没有回滚,没有隔离,没有「25 个全做或一个不做」。

如果你需要全有或全无的语义 —— 「把工单移到已归档并且把未结工单计数器减一,要么两者都不做」—— 那是 TransactWriteItems,不是批处理。事务成本更高(每个操作 按双倍计费)且上限为 100 个项,但它给你批处理刻意不提供的原子性。

处理未处理的项

一个正确的批处理调用方总是检查未处理集合并重试它。每当请求整体被接受、但某些项无法被服务时 (通常是瞬时限流),DynamoDB 就会返回 UnprocessedItems/UnprocessedKeys

只重新提交那些未处理的项,并带上 指数退避加抖动。 把批处理当作发了就不管,会悄悄丢掉写入 —— 这种 bug 几个月后才会以数据缺失的形式浮现。

在 DynoTable 中做批量写入

先用DynamoDB 定价计算器估算一个批量任务的成本 —— 一个批处理消耗的容量与它所打包的逐个写入相同,只是所用的请求数更少。

在 DynoTable 中,你在本地暂存编辑,并在把它们作为高效的批量写入提交之前先行审阅 —— 跨许多行的批量改动会以分组请求的形式发出,而不是每处改动一次 API 调用,且未处理项的 重试已替你处理好。

在 DynoTable 中把暂存的编辑作为一个批处理提交之前对其进行审阅。
在 DynoTable 中把暂存的编辑作为一个批处理提交之前对其进行审阅。

坑与后续步骤

  • 始终带退避地重试 UnprocessedItems/UnprocessedKeys —— 它们是预期之内的,不是异常。
  • 没有部分失败的回滚。 需要原子性?用事务
  • 批量写入里没有更新 —— BatchWriteItem 只有 put/delete;要改属性就动用 UpdateItem
  • 留意每次调用的上限 —— 25 个写入 / 100 个读取 / 16 MB。更大的任务要分页; 参见分页

想做批量读写却不必手写重试循环? 下载 DynoTable,直接编辑你的表。

更新于