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`);

說明

  • 那些選用鏈結不是防禦性的雜訊ResponsesUnprocessedKeys 在 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",因為立刻重試會落到同一個被節流的分割區上。
  • ConsistentReadProjectionExpression 是逐一資料表設定的,寫在每個 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

相關範例

參考資料

最後查證於 2026-07-28,對照上方連結的官方 AWS 文件。

不必透過主控台就能操作 DynamoDB

一款快速的 DynamoDB 桌面用戶端,可執行 DynamoDB 無法執行的真正 SQL — JOINs、GROUP BY、聚合 — 並支援視覺化編輯與使用你自己的 Bedrock 金鑰的 AI 代理。

30 天免費試用,無需信用卡 — 之後為無時間限制的免費方案。