Node.js 中的 DynamoDB Scan(AWS SDK v3)

底下那個 do/while 不是防禦性寫法。一個帶過濾條件的 Scan 頁面可能回來時 Items 陣列是空的,而資料表卻還沒掃完,所以在第一個回應就停下來,正是一次掃描在一張其實有符合項目的資料表上回報零筆的方式。Query 與 Scan 的比較談了何時該完全避開這個操作。

程式碼

import {DynamoDBClient, ScanCommand} from '@aws-sdk/client-dynamodb';

const client = new DynamoDBClient({region: 'us-east-1'});

const items = [];
let lastEvaluatedKey;

do {
  const response = await client.send(
    new ScanCommand({
      TableName: 'Music',
      FilterExpression: '#filter0 >= :filterValue0',
      ExpressionAttributeNames: {
        '#filter0': 'Year'
      },
      ExpressionAttributeValues: {
        ':filterValue0': {N: '2010'}
      },
      ExclusiveStartKey: lastEvaluatedKey
    })
  );

  items.push(...(response.Items ?? []));
  lastEvaluatedKey = response.LastEvaluatedKey;
} while (lastEvaluatedKey);

console.log(`Matched ${items.length} items`);

兩個空頁面、284.5 個讀取單位、8 個項目

樣本資料是 600 首歌、每首約 3.9 KB,其中剛好 8 首的 Year >= 2010,而且它們排在最後。以下是上面那個迴圈實際收到的東西:

往返次序Items.lengthScannedCount讀取單位LastEvaluatedKey
10271128.5
20271128.5
385827.5

連續兩頁什麼都沒回傳,而每頁各花 128.5 個讀取單位。寫成 if (!response.Items.length) return 的程式碼會回報一張空的資料表。API 參考把規則講得很白:"a scan result can result in no items meeting the criteria and the Count will result in zero",另外還有 "a FilterExpression is applied after the items have already been read; the process of filtering does not consume any additional read capacity units"。

第二句話請用你的帳單的方式來讀。過濾是免費的,而被它丟掉的每一樣東西都不是:284.5 個讀取單位才送出 8 個項目,和你完全不加過濾條件時付的是同一筆帳。

已於 2026-07-28 對照 9000 埠上的 DynamoDB Local(amazon/dynamodb-local),使用 node v24.18.0 上的 @aws-sdk/client-dynamodb 3.1095.0 實測。計數與容量皆為引擎自己的回應欄位。

說明

  • 第一輪時 ExclusiveStartKey: lastEvaluatedKeyundefined。v3 的序列化器會丟掉 undefined 的成員,所以同一個物件字面值就涵蓋了第一次請求與之後的每一次。改傳 {} 則會以 ValidationException: The provided starting key is invalid 失敗。
  • response.Items ?? [] 是在做真正的工作。把它和上面那張表一起看:那個空值合併運算子讓累加器在什麼都沒配對到的頁面上保持誠實,而 while 讓迴圈越過那些頁面繼續活著。
  • #filter0 不是裝飾Year 在 AWS 的保留字清單上,不做別名而直接使用會回傳 ValidationException: Invalid FilterExpression: Attribute name is a reserved keyword; reserved keyword: Year
  • Limit 算的是讀取的項目數,不是回傳的項目數。搭配這個過濾條件,Limit: 10 會產出 Count: 0ScannedCount: 10。它是應付容量尖峰的節流旋鈕,而不是要十筆結果的方式。
  • Segment / TotalSegments 會把一次全資料表掃描分散到多個工作者上。那切分的是牆鐘時間,不是成本 — 花掉的仍是同樣的 284.5 個單位,只是更快、更並行。

在真實資料表上這要花多少錢

8 個項目花 284.5 個讀取單位就是問題的形狀,而且它是隨資料表線性成長,不是隨結果成長。在把一次帶過濾條件的掃描放上熱路徑之前,先在 DynamoDB 定價計算機裡以你的項目大小與流量替完整讀取標價,再拿它跟一個能把同樣存取模式變成 Query 的 GSI 比較。

想在 GUI 中探索資料表,並取得帶過濾與分頁的結果格線,就下載 DynoTable,別再從腳本裡盲目掃描。

相關指南

參考資料

以視覺化方式建構此請求

在免費的 DynamoDB 查詢建構器中組合此操作 — 鍵條件、Filter、Index、Limit、排序方向與分頁迴圈 — 再把它複製成可執行的 SDK v3、CLI 或 boto3 程式。

開啟 DynamoDB 查詢建構器

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

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

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