Node.js 中的 DynamoDB BatchGetItem(AWS SDK v3)
BatchGetItem 在一次请求里按主键取回多达 100 个项目。在 AWS SDK v3 里,这个调用必须写成循环,因为 UnprocessedKeys 是随一次_成功_的响应回来的,而不是随错误回来的。什么会把它填满,以及为什么 16 MB 和每分区 1 MB 才是要紧的数字,DynamoDB 批量操作里讲过。本页讲的是 v3 的这个调用,以及它交还给你的东西。
代码
import {BatchGetItemCommand, DynamoDBClient} from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({region: 'us-east-1'});
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
let requestItems = {
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'}}
]
}
};
const items = [];
let attempt = 0;
do {
const response = await client.send(new BatchGetItemCommand({RequestItems: requestItems}));
items.push(...(response.Responses?.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.
requestItems = response.UnprocessedKeys;
if (requestItems && Object.keys(requestItems).length > 0) {
attempt += 1;
await sleep(Math.min(100 * 2 ** attempt, 5000));
}
} while (requestItems && Object.keys(requestItems).length > 0);
console.log(`Fetched ${items.length} items`);说明
- 那些可选链不是防御性的噪音。
Responses和UnprocessedKeys在 v3 的类型里都是可选的,所以response.Responses?.Music ?? []和那个Object.keys()守卫正是编译器要求的东西。在纯 JavaScript 里,它们是拦住第一个空响应抛错的东西。 - 按
err.name分支。v3 把服务端错误码放在那里,而这个命令真正会抛出的两种失败都不可重试,所以它们绝不能掉进退避循环里。超过 100 个键会得到ValidationException/Too many items requested for the BatchGetItem call;同一个键给两次会得到Provided list of item keys contains duplicates。两者都在 Python 页面上原样复现过。 UnprocessedKeys回来时就已经是RequestItems的形状,这也是循环能直接把它赋回去的唯一原因。它不是分页游标,也不意味着调用失败了。- 退避是 AWS 的指示,不是锦上添花。API 参考告诉你要用"an exponential backoff algorithm",因为立刻重试会落到同一个被限流的分区上。
ConsistentRead和ProjectionExpression是按表设置的,写在每个RequestItems条目内部,而不是顶层。当这个映射只有一个键、看起来像个扁平请求时,这一点很容易漏掉。
响应实际返回的样子
针对三首歌都在的 DynamoDB Local 3.3.0 运行上面的代码,加上 ReturnConsumedCapacity: 'TOTAL',并打印歌名而不是数量:
order: [ 'A Mis Abuelos', 'Misty', 'Cubano Chant' ]
UnprocessedKeys: {}
ConsumedCapacity: [ { TableName: 'Music', CapacityUnits: 1.5 } ]请求列的顺序是 Cubano Chant、A Mis Abuelos、Misty。响应一个位置都没对上,这正是循环要往一个扁平数组里 push、而不是按下标取值的原因。用键属性把项目和请求对回去,并把这些键包含进任何 ProjectionExpression 里,好让你还有东西可以对。
在同一个 Music 条目上设置 ConsistentRead: true,同样这三个键就要花 3 个单元而不是 1.5 个。三个各自小于 4 KB 的项目会被当成三次独立的 GetItem 读取来计费,最终一致性读每次半个单元,强一致性读每次一个单元。定价计算器会在你敲定读取模式之前,把这种按项目的算术换算成一个月度数字。
现在删掉 Misty 再跑一次:两个项目、一个空的 UnprocessedKeys,以及 1.0 个单元。那个缺失的键一分钱没算。这是 DynamoDB Local 的产物,不是契约;BatchGetItem 参考(2026-07-28 取回)写明,对不存在的项目的请求仍会按该读取类型消耗最低读取容量。别拿本地运行来给一批缓存未命中做容量规划。
想把一组键读回来、看看到底返回了什么,又不想先写这个循环,就下载 DynoTable。
相关示例
- Python 中的 DynamoDB BatchGetItem——用 boto3 做同样的批量读取。
- 用 AWS CLI 执行 DynamoDB BatchGetItem——从命令行做同样的批量读取。
- Node.js 中的 DynamoDB GetItem——被它批量化的那次单项目读取。
- DynamoDB 批量操作——限制、部分失败,以及批量在什么时候划算。
- "Too many items requested for the BatchGetItem call"——一次请求里超过 100 个键。
- "Provided list of item keys contains duplicates"——一个批次里同一个键出现了两次。
参考资料
- BatchGetItem — Amazon DynamoDB API Reference
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
最后核实于 2026-07-28,依据上方链接的 AWS 官方文档。