DynamoDB Batch Write in Python (boto3 batch_writer)
batch_writer() ist der eine DynamoDB-Aufruf, bei dem Python weniger Arbeit macht als die anderen SDKs. Es puffert Puts und Deletes, schneidet sie in BatchWriteItem-Anfragen zu je 25 und sendet unverarbeitete Items selbst erneut. Was es nicht tut: dich vor den zwei Fehlern schützen, an denen die meisten Massen-Ladeläufe scheitern — und beide tauchen erst beim Flush auf, nicht in der Zeile, die das schlechte Item geliefert hat.
Code
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")Erklärung
- Verzögerter Flush —
batch.put_item()hängt an eine Liste an. Nichts wird validiert, serialisiert oder gesendet, bis der Puffer 25 erreicht oder derwith-Block endet. Der Traceback für ein fehlerhaftes Item kommt also vom Flush und nicht von demput_item-Aufruf, der es geliefert hat. Wenn du aus einem Iterator lädst, führe selbst Buch darüber, was in den Puffer gewandert ist. - Schlichte Python-Werte — das ist die Resource-API, du schreibst also
1994, nicht{"N": "1994"}. Für alles Gebrochene sind Decimals Pflicht; einfloatwird in den Puffer aufgenommen und beim Flush abgelehnt. batch_writer()ist eineTable-Methode. Das Gegenstück auf der Leseseite ist es nicht:batch_get_itemlebt auf derServiceResource, undtable.batch_get_itemexistiert nicht. Für Batch-Reads gibt es überhaupt keinen Puffer-, Chunking- oder Retry-Helfer.UnprocessedItems, keine Fehler — mehr wiederholt es nicht. Ein gedrosselter Write wird erneut gesendet; eineValidationExceptionpropagiert. Wer stattdessen überclient.batch_write_itemgeht, bekommt die ganze Schleife selbst in die Hand, wie im Node.js-Beispiel.- Es kann die Service-Limits nicht aufheben. 25 Writes pro Anfrage, 400 KB pro Item, 16 MB pro Anfrage, keine Bedingungen und keine Updates — und jeder Put ersetzt das gespeicherte Item vollständig. Du brauchst einen Guard oder Alles-oder-nichts? TransactWriteItems.
Was batch_writer beim Flush tatsächlich tut
Puffere 30 Puts und schau dir an, welche Aufrufe es macht. table.meta.client.batch_write_item umwickelt und die Anfragegrößen mitgeschrieben, gegen DynamoDB Local 3.3.0:
batch sizes sent: [25, 5]Zwei Anfragen, am Service-Limit geschnitten, mit dem Rest per __exit__ geflusht. Dieser Flush ist bedingungslos: Löse eine RuntimeError im Block aus, und die gepufferten Items werden auf dem Weg nach draußen trotzdem geschrieben. Ein Massen-Ladelauf, der auf halber Strecke stirbt, hinterlässt also einen Teilbestand, keinen sauberen Zustand.
Jetzt die zwei Fehler. Puffere denselben Key zweimal, was in dem Moment passiert, in dem deine Quelldaten eine Dublette enthalten:
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 duplicatesKeines der beiden put_item hat sich beschwert. batch_writer() dedupliziert nicht, solange du es nicht verlangst — und verlangen heißt table.batch_writer(overwrite_by_pkeys=["Artist", "SongTitle"]). Lass dieselben zwei Puts damit laufen, und das Item wird als Year: 2 gespeichert: Der Puffer behält pro Key den letzten Write, die Deduplizierung ist also stiller Datenverlust, falls deine zwei Zeilen eigentlich verschiedene Items unter einem Key sein sollten, den du falsch gewählt hast.
Der zweite gehört boto3 allein und erreicht DynamoDB nie:
TypeError: Float types are not supported. Use Decimal types instead.Ein Rating von 4.5 sitzt klaglos im Puffer und fliegt beim Flush auf. Decimal("4.5") läuft korrekt als {"N": "4.5"} durch. Liest du einen Preis oder eine Bewertung mit json.loads aus JSON, ist jede Zahl ein float — für die meisten Import-Skripte ist das also ein Fehlschlag beim allerersten Lauf. parse_float=Decimal an json.loads übergeben behebt es an der Quelle.
Wenn du zwischen nativen Python-Werten und dem Wire-Format von Hand hin- und herwechselst, zeigt der DynamoDB-JSON-Konverter beide Seiten desselben Items, sodass du siehst, was aus deinem Decimal tatsächlich wird.
Um aus CSV oder JSON massenhaft zu laden, ohne das Typ-Mapping selbst zu schreiben, lade DynoTable herunter.
Verwandte Beispiele
- DynamoDB BatchWriteItem in Node.js — die manuelle Retry-Schleife, die batch_writer versteckt.
- DynamoDB BatchWriteItem mit der AWS CLI — derselbe Batch-Write aus der Shell.
- DynamoDB PutItem in Python — der Einzel-Item-Write, den das hier bündelt.
- Batch-Operationen in DynamoDB — Limits, Teilausfälle und wann sich Batching lohnt.
- "Too many items requested for the BatchWriteItem call" — mehr als 25 Put-/Delete-Anfragen in einem Batch.
- "Provided list of item keys contains duplicates" — zwei Anfragen auf denselben Key in einem Batch.
Referenzen
- Amazon DynamoDB guide (batch_writer) — Boto3 documentation
- BatchWriteItem — Amazon DynamoDB API Reference
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
Zuletzt verifiziert am 2026-07-28 gegen die oben verlinkte offizielle AWS-Dokumentation.