Python(boto3)中的 DynamoDB Scan

boto3 裡的一次 scan 是兩個決定:請求本身,以及你怎麼分頁。下面的程式碼用的是內建分頁器,那不是別人繞著你的迴圈寫出來的包裝。它是五行 botocore 設定,而那五行決定了你的掃描是否正確、以及它要花多少錢。(至於你到底該不該掃描,那是另一個問題。)

程式碼

import boto3

client = boto3.client("dynamodb")

paginator = client.get_paginator("scan")

items = []
for page in paginator.paginate(
    TableName="Music",
    FilterExpression="#filter0 >= :filterValue0",
    ExpressionAttributeNames={"#filter0": "Year"},
    ExpressionAttributeValues={":filterValue0": {"N": "2010"}},
):
    items.extend(page["Items"])

print(f"Matched {len(items)} items")

說明

  • 分頁器是資料,不是程式碼。botocore 在 paginators-1.json 裡為每個操作各放一筆;Scan 的那一筆是 {"input_token": "ExclusiveStartKey", "output_token": "LastEvaluatedKey", "limit_key": "Limit", "result_key": ["Items", "Count", "ScannedCount"], "non_aggregate_keys": ["ConsumedCapacity"]}。以下的一切都是從那些 key 推導出來的。
  • PaginationConfig={"PageSize": n} 設定的是 Limit,因為 Limit 就是那個 limit_keyLimit 限制的是被讀取的項目數,永遠不是被回傳的項目數,所以搭配 FilterExpression 時,一頁可以是空的、卻仍然有成本。
  • MaxItems 算的是 result_key 的項目數,並交還一個 NextToken,讓你在之後的行程裡當成 StartingToken 傳入。它不會阻止請求讀過你設定的截止點。
  • build_full_result() 只彙總 result_key 那些欄位ItemsCountScannedCount 會被加總;ConsumedCapacitynon_aggregate_key,所以合併後的結果會把單一頁的容量當成整次掃描的容量來回報。請自己逐頁加總,否則你少報的倍數就是頁數。
  • FilterExpression 是在讀取之後於伺服器端執行,所以你是按 ScannedCount 而不是 Count 計費。#filter0Year 取別名,是因為它是一個保留字;沒有那個別名,請求在讀到任何東西之前就會失敗。
  • 錯誤一律以 botocore.exceptions.ClientError 抵達。請依 e.response["Error"]["Code"] 分支;個別錯誤的類別只以用戶端上動態產生的屬性形式存在(client.exceptions.ProvisionedThroughputExceededException),從來不是可以匯入的符號。
  • 資源 API 是另一種使用手感Table.scan 接受原生 Python 型別、以 decimal.Decimal 回傳數字,並用 Attr("Year").gte(2010) 而不是佔位符對應來組出 filter。

一頁帶 filter 的掃描實際花多少

60 個項目,每個約 2 KB,其中 Year = 2024 命中兩個,PageSize=10,對 DynamoDB Local 執行:

page 1: Count=0 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 2: Count=1 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 3: Count=0 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 4: Count=1 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 5: Count=0 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 6: Count=0 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 7: Count=0 ScannedCount=0 CU=0.0 LastEvaluatedKey=no
total CU across pages: 15.0

六個真實頁面中有四頁什麼都沒回傳,卻付了全額。那就是分頁器存在所要防止的那種 bug 的樣貌:一個在 Items 為空時就中斷的自刻迴圈,會在第 1 頁退出,並把兩首符合的歌回報成零。

第 7 頁是另一半的故事。第 6 頁在表格的最後一個項目上碰到它的 Limit,所以 DynamoDB 照樣回傳了一個 LastEvaluatedKey,於是分頁器多花了一趟來回,才知道已經沒東西了。LastEvaluatedKey 的意思是「我停下來了」,不是「還有更多」。

對同一次掃描呼叫 build_full_result() 回報的是 CapacityUnits: 2.5。那六頁消耗的是 15.0。

不用自己寫迴圈的分頁方式

DynamoDB 查詢建構器會把 filter、別名對應與分頁迴圈組成一支可執行的程式,所以上面那個 LimitCount 的陷阱在你貼上之前就已經處理好了。若想互動式地翻閱一張真正的表格,而不是從指令碼跑,請下載 DynoTable

相關指南

參考資料

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

以視覺化方式建構此請求

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

開啟 DynamoDB 查詢建構器

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

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

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