Python 中的 DynamoDB Query(boto3)
boto3 的 query 分頁器正是本頁很短的原因:它把 LastEvaluatedKey 完全藏了起來。它也藏起了一個你多半想要的數字,而那才是在信任它之前值得知道的部分。至於究竟何時該動用 query,請見 Query 與 Scan 的比較。
程式碼
import boto3
client = boto3.client("dynamodb")
paginator = client.get_paginator("query")
items = []
for page in paginator.paginate(
TableName="Music",
KeyConditionExpression="#hashKey = :hashKeyValue AND begins_with(#rangeKey, :rangeKeyValue)",
ExpressionAttributeNames={"#hashKey": "Artist", "#rangeKey": "SongTitle"},
ExpressionAttributeValues={":hashKeyValue": {"S": "Arturo Sandoval"}, ":rangeKeyValue": {"S": "C"}},
):
items.extend(page["Items"])
print(f"Found {len(items)} items")分頁器不會替你把帳單加總
針對一個 600 首歌的樣本資料、每首歌約 3.9 KB 且全都在 Artist = "Arturo Sandoval" 之下,這個迴圈產出三頁:271、271 與 58 個項目,分別花費 128.5、128.5 與 27.5 個讀取單位。向同一個分頁器要一份合併結果,你會得到這個:
build_full_result() -> Items 600 Count 600 ScannedCount 600
ConsumedCapacity.CapacityUnits 128.5Count 與 ScannedCount 被加總了。ConsumedCapacity 沒有 — 它是第一頁的數字,而真正的總數是 284.5。botocore 的 DynamoDB 分頁器設定明白說出了原因:Count 與 ScannedCount 被列為結果鍵,ConsumedCapacity 則是非彙總鍵。如果你是從 build_full_result() 記錄容量,那你對一次整個分割區讀取的回報少了一半以上。
上面 for page in paginator.paginate(...) 迴圈裡那些逐頁的 dict 是原始回應,所以自己把 page["ConsumedCapacity"]["CapacityUnits"] 加起來,就能得到誠實的 284.5。
那個讓你多跑 58 次往返的 Limit
Limit 是一個合法的 query 參數,所以 paginate() 接受它,而它並不是 Python 使用者以為的那個參數:
paginate(..., Limit=10) -> 61 pages, 10 items each
paginate(...) -> 3 pages它限制的是每次請求的項目數,而不是總數,所以分頁器就盡責地發出 61 次 HTTP 呼叫來取回同樣那 600 個項目。要限制總數,請用 PaginationConfig={"MaxItems": 10};PaginationConfig["PageSize"] 才是對應到 Limit 的那個旋鈕。
已於 2026-07-28 對照 9000 埠上的 DynamoDB Local(amazon/dynamodb-local),使用 CPython 3.14.6 上的 boto3 1.43.58 實測。
說明
- client 在兩個方向上都說 DynamoDB JSON。值以
{"S": "Arturo Sandoval"}的形式進去,而Year以{"N": "1994"}的形式回來。而 resource API(boto3.resource("dynamodb").Table(...).query)會雙向轉換,並交給你Decimal('1994')— 那對金額是正確的,而第一次它拒絕跟一個float相加時會讓你意外。 Key("Artist").eq(...)只屬於 resource API。把它傳給 client 會在請求送出前就拋出錯誤:ParamValidationError: Invalid type for parameter KeyConditionExpression ... valid types: <class 'str'>。client 要的是本頁組出來的那個運算式字串。- 索引鍵條件是一個等式加上最多一個排序索引鍵比較(
=、<、<=、>、>=、BETWEEN、begins_with)。其他任何東西都放進FilterExpression,boto3 會原樣傳過去,而 DynamoDB 會在讀取之後才套用它。ScanIndexForward=False會反轉順序,IndexName="..."會改指向某個索引。
改用視覺化操作
DynamoDB Expression Builder 會把索引鍵條件與具型別的 ExpressionAttributeValues 對應寫成可直接給 boto3 用的 Python,而那正是你打成 {"N": 2010} 而不是 {"N": "2010"} 時會出錯的部分。
想從一個索引鍵條件表單,把同一個查詢指向你自己的資料表,並在分頁格線中讀取結果,就下載 DynoTable。
相關指南
- Query 與 Scan 的比較 — 為什麼
query才是對的預設。 - 索引鍵條件運算式 — 每一個合法的分割區/排序索引鍵運算子。
- 「Query condition missed key schema element」 — 索引鍵條件點名了錯的屬性,或跳過了分割區索引鍵。
- 「Query key condition not supported」 — 索引鍵條件不能用的運算子,例如 contains 或第二個排序索引鍵條件。