Python 中的 DynamoDB 批次寫入(boto3 batch_writer)
batch_writer() 是唯一一個 Python 比其他 SDK 更省事的 DynamoDB 呼叫。它會緩衝 put 與 delete、把它們切成每批 25 筆的 BatchWriteItem 請求,並自行重送未處理的項目。它沒有替你擋掉的,是那兩個毀掉大多數大量載入的失敗,而且兩者都在清空緩衝時才浮現,而不是在提供那筆壞項目的那一行。
程式碼
import boto3
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("Music")
songs = [
{"Artist": "Arturo Sandoval", "SongTitle": "Cubano Chant", "AlbumTitle": "Danzon", "Year": 1994},
{"Artist": "Arturo Sandoval", "SongTitle": "A Mis Abuelos", "AlbumTitle": "Danzon", "Year": 1994},
{"Artist": "Arturo Sandoval", "SongTitle": "Groovin' High", "AlbumTitle": "Swingin'", "Year": 1996},
]
with table.batch_writer() as batch:
for song in songs:
batch.put_item(Item=song)
# batch_writer buffers deletes too — target a key you're NOT also putting
# (two writes to the same key in one batch are rejected as a duplicate)
batch.delete_item(Key={"Artist": "Ella Fitzgerald", "SongTitle": "Misty"})
print(f"Buffered {len(songs)} puts + 1 delete; the batch flushes on exit")說明
- 延後清空 —
batch.put_item()只是把項目附加到一個 list。在緩衝區累積到 25 筆、或with區塊結束之前,什麼都不會被驗證、序列化或送出,所以壞項目的 traceback 來自清空緩衝的那一刻,而不是提供它的那次put_item呼叫。如果你是從迭代器載入,請自己記錄有哪些東西進了緩衝區。 - 純 Python 值 — 這是 resource API,所以你寫的是
1994,不是{"N": "1994"}。任何帶小數的值都必須用 Decimal;float會被收進緩衝區,然後在清空時被拒絕。 batch_writer()是Table的方法。讀取端的對應物則不是:batch_get_item掛在ServiceResource上,table.batch_get_item並不存在。批次讀取完全沒有緩衝、切批或重試的輔助工具。UnprocessedItems,而不是錯誤 — 它只處理這一種重試。被節流的寫入會重送;ValidationException則會往外拋。改走client.batch_write_item就得自己寫整個迴圈,就像 Node.js 範例那樣。- 它無法解除服務限制。每個請求 25 筆寫入、每筆項目 400 KB、每個請求 16 MB,不支援條件也不支援更新,而且每次 put 都會整筆取代已存的項目。需要保護條件,或要全有全無?請用 TransactWriteItems。
batch_writer 在清空緩衝時實際做了什麼
緩衝 30 筆 put,看看它實際發出哪些呼叫。包裝 table.meta.client.batch_write_item 並記錄請求大小,對照 DynamoDB Local 3.3.0:
batch sizes sent: [25, 5]兩個請求,切在服務限制上,剩下的由 __exit__ 清出。那次清空是無條件的:在區塊內拋出 RuntimeError,緩衝中的項目照樣會在離開時被寫入。中途死掉的大量載入留下的是寫了一半的資料,而不是乾淨的狀態。
現在來看那兩個失敗。把同一個鍵緩衝兩次,這正是你的來源資料一出現重複就會發生的事:
with table.batch_writer() as batch:
batch.put_item(Item={"Artist": "Dup", "SongTitle": "Key", "Year": 1})
batch.put_item(Item={"Artist": "Dup", "SongTitle": "Key", "Year": 2})botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the
BatchWriteItem operation: Provided list of item keys contains duplicates兩次 put_item 都沒有抱怨。batch_writer() 不會自動去重,除非你明講,而明講的方式是 table.batch_writer(overwrite_by_pkeys=["Artist", "SongTitle"])。把同樣那兩筆 put 交給它,項目最後會存成 Year: 2 — 緩衝區只保留每個鍵的最後一次寫入,所以如果你那兩列本來該是不同的項目、只是鍵設錯了,這個去重就是無聲的資料遺失。
第二個失敗完全是 boto3 自己的,根本到不了 DynamoDB:
TypeError: Float types are not supported. Use Decimal types instead.一個值為 4.5 的 Rating 會安安靜靜地待在緩衝區裡,然後在清空時爆炸。Decimal("4.5") 則能正確往返成 {"N": "4.5"}。用 json.loads 從 JSON 讀出價格或評分時,每個數字都是 float,所以這對大多數匯入腳本來說是第一次執行就會踩到的失敗。把 parse_float=Decimal 傳給 json.loads 就能從源頭修掉它。
如果你是手動在原生 Python 值與傳輸格式之間搬移,DynamoDB JSON 轉換器會同時顯示同一筆項目的兩種樣貌,讓你看到你的 Decimal 實際變成了什麼。
想從 CSV 或 JSON 大量載入、又不想自己寫型別對應,請下載 DynoTable。
相關範例
- Node.js 中的 DynamoDB BatchWriteItem — batch_writer 藏起來的那個手動重試迴圈。
- 使用 AWS CLI 的 DynamoDB BatchWriteItem — 從 shell 做同一次批次寫入。
- Python 中的 DynamoDB PutItem — 這裡被批次化的單筆寫入。
- DynamoDB 的批次操作 — 限制、部分失敗,以及批次何時划算。
- 「Too many items requested for the BatchWriteItem call」 — 一個批次裡超過 25 個 put/delete 請求。
- 「Provided list of item keys contains duplicates」 — 一個批次裡有兩個請求碰到同一個鍵。
參考資料
- Amazon DynamoDB guide (batch_writer) — Boto3 documentation
- BatchWriteItem — Amazon DynamoDB API Reference
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
最後驗證於 2026-07-28,對照上方連結的 AWS 官方文件。