Python(boto3)中的 DynamoDB BatchGetItem

batch_get_item 會在一次請求中,依主索引鍵取回最多 100 個項目。下方程式碼裡的 while request_items: 就是整個 boto3 慣用寫法:DynamoDB 會在一次_成功_的回應裡把剩下沒處理的交還給你,而空字典是 falsy,所以迴圈會自己結束。限制與部分結果的規則放在 DynamoDB 的批次操作;這一頁談的是 boto3 的呼叫本身,以及它會丟出的錯誤。

程式碼

import time

import boto3

client = boto3.client("dynamodb")

request_items = {
    "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"}},
        ]
    }
}

items = []
attempt = 0

while request_items:
    response = client.batch_get_item(RequestItems=request_items)
    items.extend(response["Responses"].get("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.
    request_items = response["UnprocessedKeys"]
    if request_items:
        attempt += 1
        time.sleep(min(0.1 * 2**attempt, 5))

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

說明

  • response["UnprocessedKeys"] 永遠都在。在完全服務完的批次上,這個鍵仍然存在、值為 {},所以 request_items = response["UnprocessedKeys"] 可以安全地取用,而那個 falsy 的空字典正是結束 while 的東西。真正要小心的是 Responses:如果某張資料表的索引鍵全都沒命中,它就不會出現在裡面,這也是程式碼裡用 .get("Music", []) 的原因。
  • ConsistentReadProjectionExpression 要放在每張資料表各自的字典裡,跟 "Keys" 並排,而不是跟 RequestItems 並排。boto3 會很樂意把放錯位置的鍵送出去,然後讓服務去回絕它。
  • 這是低階 client,所以值是 DynamoDB JSON{"S": ...}{"N": ...})。resource API 的 batch_get_item 掛在 ServiceResource 上,而不是 Table 上。boto3.resource("dynamodb").batch_get_item(...) 收的是原生 Python 值;table.batch_get_item 根本不存在。這種不對稱會讓用過 table.batch_writer() 之後順手去找它的人吃一驚,因為後者_確實_是 Table 的方法。
  • 退避只適用於 UnprocessedKeysValidationException 是請求本身的臭蟲,重試它只是在燒時鐘。

這次呼叫會丟出的兩個錯誤,逐字原文

兩者都是用戶端的錯誤,重試都救不了,而且都以單純的 botocore.exceptions.ClientError 浮現。在 DynamoDB Local 3.3.0 上,str(e) 是:

An error occurred (ValidationException) when calling the BatchGetItem operation: Too many items requested for the BatchGetItem call
An error occurred (ValidationException) when calling the BatchGetItem operation: Provided list of item keys contains duplicates

第一個是 101 個索引鍵,第二個是同一個索引鍵列了兩次。請注意你不能這樣寫來攔截它們:

except client.exceptions.ValidationException:  # AttributeError

botocore 在 DynamoDB client 上模型化了 34 個具名例外類別,而 ValidationException 不在其中。ConditionalCheckFailedExceptionProvisionedThroughputExceededException 都在,這也是為什麼條件式寫入那一頁可以依類別攔截,而這一頁不行。甚至還有一個模型化的 DuplicateItemException,而它不是批次裡出現重複索引鍵時你會拿到的東西。所以批次讀取只能依錯誤代碼分支:

except ClientError as e:
    if e.response["Error"]["Code"] == "ValidationException":
        raise  # a bug in the request; retrying will not help

重複索引鍵才是真實程式碼裡會咬人的那一個,因為從 Query 結果或關聯表組出來的索引鍵清單,本來就很自然會重複。送出前先去重,並記得:兩個字典要每一個索引鍵屬性都相同才算相等。

大小是 100 個項目的批次留不住 100 個的另一個原因:每個項目在計費時會無條件進位到 4 KB,並計入回應的 16 MB 上限,所以 100 個 300 KB 的項目大約只會回來 52 個,其餘落在 UnprocessedKeys 裡。項目大小計算機會給你可以拿來相乘的每項目數字。

想在寫那個迴圈之前先把一組索引鍵拉回來、看看到底回了什麼,請下載 DynoTable

相關範例

參考資料

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

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

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

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