入門閱讀時間 3 分鐘

DynamoDB 中的分頁

DynamoDB 絕不會在一次呼叫中傳回「所有」結果。一個 QueryScan 最多傳回 1 MB 的資料,然後交給你一個 LastEvaluatedKey 以便接續。要正確分頁,意味著在那個鍵上迴圈 — 而非在計數器上。

迴圈

let key;
do {
  const out = await client.send(new QueryCommand({...params, ExclusiveStartKey: key}));
  process(out.Items);
  key = out.LastEvaluatedKey;
} while (key);

LastEvaluatedKeyundefined 時,你就抵達了結尾。將它作為 ExclusiveStartKey 傳回,以擷取下一個切片。

控制流程就是一個單一迴圈,只有在鍵不存在時才會退出:

presentabsentQuery / ScanProcess ItemsLastEvaluatedKey?Set ExclusiveStartKeyDone

每一輪要嘛從傳回的鍵接續,要嘛停止 — 沒有任何計數器。

Limit 不是頁面大小

Limit 限制的是 DynamoDB 評估多少項目,而非它在 FilterExpression 之後傳回多少。一個帶有 filter 的 Limit: 25 查詢可能傳回 3 個項目,卻仍交給你一個 LastEvaluatedKey — 你必須持續分頁直到該鍵為空,即使某一頁看起來很短也一樣。非空LastEvaluatedKey 也絕不保證還有更多相符的項目;只有不存在的鍵才證明你已抵達結尾。

讓 SDK 來分頁

兩個 SDK 都包裝了上面的迴圈,讓你能直接迭代各頁:

// AWS SDK for JavaScript v3
import {paginateQuery} from '@aws-sdk/lib-dynamodb';
for await (const page of paginateQuery({client}, params)) {
  process(page.Items);
}
# boto3
paginator = client.get_paginator('query')
for page in paginator.paginate(**params):
    process(page['Items'])

沒有頁碼

DynamoDB 沒有總計數,也沒有隨機頁面存取 — 你無法在不重播游標的情況下跳到「第 7 頁」或往回翻。請圍繞無限捲動/「載入更多」來設計 UI,而非編號頁面。(一個 Select: 'COUNT' 查詢仍會讀取 — 並為此計費 — 每個相符的項目來計數。)

為 API 設計的無狀態游標

LastEvaluatedKey 只是最後一個項目的鍵屬性。將它以 base64 編碼,作為不透明的 nextToken 交給用戶端;在下一個請求中再把它解碼回 ExclusiveStartKey。沒有伺服器端的游標狀態。

那個 token 是 DynamoDB-JSON — 用 DynamoDB-JSON 轉換器目視或手動製作一個。而如果你分頁是為了繞過 Scan,那通常是個應該改為新增索引的訊號。

試用 DynoTable,以視覺化方式翻閱查詢結果,並由它為你追蹤游標。

已更新