Warum ein DynamoDB-Scan langsam und teuer ist
Ein Scan liest jedes Item in der Tabelle und filtert erst danach. Es ist die
Operation, zu der du aus SQL-Muskelgedächtnis greifst, und die, die leise deine
Rechnung hochtreibt, während sie deine Latenz schlechter macht als die RDS-Box, die
du verlassen hast.
Warum ist mein DynamoDB-Scan langsam und teuer?
Ein Scan liest jedes Item in der Tabelle, bevor die FilterExpression läuft — du zahlst also für das Lesen der gesamten Tabelle, egal wie wenige Zeilen zurückkommen, und er wird langsamer, je mehr die Tabelle wächst. Der Fix ist fast immer eine gekeyte Query — modelliere das Zugriffsmuster um einen Key herum, damit DynamoDB nur eine Partition statt alles liest.
- Ein
Scanliest die ganze Tabelle, jedes Mal. Die Größe, nicht deine Ergebnis-Anzahl, entscheidet, was du zahlst und wie lange es dauert. - Die
FilterExpressionist eine Lüge über die Kosten. Sie läuft, nachdem der Read gemessen ist, also kann das Zurückgeben von 12 Items das Lesen von 12 Millionen abrechnen. - Ein
Scanwird langsamer, während du wächst. Eine gekeyteQuerybleibt flach — sie berührt eine Partition, egal wie groß die Tabelle wird. - Der Fix ist fast immer Modellierung, kein Tuning. Wenn du
Scanst, um eine Routinefrage zu beantworten, fehlt dir ein Key.
Was ein Scan tatsächlich tut
Aus SQL kommend fühlt sich SELECT * FROM events WHERE type = 'checkout' gratis an
— die Engine hat einen Index, oder sie hat keinen, aber so oder so bekommst du
Zeilen zurück. In DynamoDB gibt es keinen Query-Planner, der das für dich
entscheidet.
Ein Scan läuft die ganze Tabelle sequenziell ab, 1 MB auf einmal, und reicht
jede Seite an deine FilterExpression. Was immer der Filter ablehnt, wird trotzdem
gelesen, trotzdem gemessen und steht trotzdem auf deiner Rechnung. (AWS: Scanning
tables)
Das ist die Falle. Der Filter sieht aus wie eine WHERE-Klausel, aber er ändert
die Ergebnismenge, nie die Kosten. Ein Scan verbraucht dieselbe Read Capacity,
ob ein Filter vorhanden ist oder nicht. (AWS: Scanning tables)
Zähl die Read Units
DynamoDB misst Reads in Read Capacity Units (RCUs). Eine RCU kauft einen einzelnen stark konsistenten Read eines Items bis 4 KB; letztendlich konsistente Reads kosten die Hälfte davon. Größere Items runden auf die nächsten 4 KB auf. (AWS: Read/write capacity mode)
Nimm eine Analytics-Tabelle, ProductEvents. Jede Zeile ist ein getracktes Event:
PK = "TENANT#acme"
SK = "TS#2026-06-23T14:08:55Z#evt_9f3a"
attrs: eventType, sessionId, userId, payloadBytesSagen wir, sie hält 2.000.000 Events, jedes ~1 KB, alle unter einem beschäftigten Tenant. Du willst die heutigen Checkouts. Der reflexhafte Zug:
Scan ProductEvents
FilterExpression: eventType = "checkout"
Dieser Filter gibt vielleicht 40 Zeilen zurück. Aber der Scan las zuerst alle
2.000.000 Items. Bei ~1 KB pro Stück (1 RCU pro 4 KB, letztendlich konsistent ≈ 0,5
RCU pro 4 KB) hast du rund 250.000 RCUs gemessen — und ~500 MB Daten
durchpaginiert — um 40 Items zurückzugeben.
Modelliere nun das Zugriffsmuster als Key und Query es stattdessen:
Query ProductEvents
PK = "TENANT#acme"
AND SK begins_with "TS#2026-06-23"
Das liest nur die gematchte Scheibe einer Partition. Wenn diese 40 Checkout-Zeilen plus die anderen Events des Tages auf ~2 MB kommen, zahlst du für ~2 MB Reads, nicht 500 MB. Dieselbe Antwort, ein winziger Bruchteil der Kosten — und die Latenz bleibt flach, während die Tabelle wächst.
Scan vs. Query, gemessen
| Scan + Filter | Gekeyte Query | |
|---|---|---|
| Reads | Jedes Item in der Tabelle | Eine Partition, per SK eingeengt |
| Abgerechnete Kapazität | Ganze Tabelle, vor dem Filter | Nur die Items in deiner Scheibe |
| Unser Beispiel | ~250.000 RCUs (~500 MB) | ein paar hundert RCUs (~2 MB) |
| Latenz | Wächst mit der Tabellengröße | Flach, während die Tabelle wächst |
| Ergebnis-Anzahl | Entscheidet nichts über die Kosten | Entspricht dem, was du zahlst |
Die Lektion, die die Tabelle kodiert: bei einem Scan sind deine Ergebnis-Anzahl
und deine Rechnung unzusammenhängend. Bei einer Query folgen sie einander.
Entscheide, bevor du Scan'st
Die meisten versehentlichen Scans kommen aus einer Frage: kann ich die
Partition benennen, die ich brauche? Wenn ja, ist es eine Query. Wenn nein, ist
der Fix ein Key, kein größerer Filter.
Hier ist die Entscheidung in Flussform.
Der Pfad endet fast immer bei Query; du fällst nur zu Scan durch, wenn kein Key
— vorhanden oder hinzufügbar — zum Zugriffsmuster passt.
Wenn das Muster real und wiederkehrend ist, aber die Basistabelle es nicht keyen
kann, ist das das Signal, einen Global Secondary Index
hinzuzufügen, sodass die Frage zu einer Query wird. Deine Keys von vornherein um
deine Zugriffsmuster zu modellieren ist das ganze Spiel — siehe
Single-Table Design.
Schreib die gekeyte Query, nicht einen Filter
Wenn du eine Bedingung über den Key hinaus brauchst, baue sie bewusst, statt alles
in eine FilterExpression zu kippen. Der
DynamoDB Expression Builder generiert die
KeyConditionExpression und Attribut-Platzhalter für dich, sodass Partition und
Sort Key das Einengen machen — bevor DynamoDB den Read misst, nicht danach.
KeyConditionExpression: PK = :tenant AND begins_with(SK, :day)
Wann ein Scan tatsächlich in Ordnung ist
Ein Scan ist nicht verboten — er ist nur der falsche Default. Er ist das richtige
Werkzeug, wenn du wirklich "lies alles" meinst:
- Einmalige Exporte oder von Hand ausgeführte Backfills.
- Winzige Config-/Lookup-Tabellen, wo die ganze Tabelle ein paar KB ist.
- Background-Jobs, die die volle Tabelle absichtlich paginieren. Splitte die
über Worker mit
Segment/TotalSegments— einem Parallel Scan — statt eines langen sequenziellen Crawls. (AWS: Scanning tables)
Und beachte, dass PartiQL dich nicht rettet: SELECT * FROM ProductEvents WHERE eventType = 'checkout' ohne Key-Prädikat kompiliert direkt zu einem Scan. Es ist
dieselbe Falle in SQL-Kleidung. (Siehe Query vs. Scan für
die volle Aufschlüsselung.)
Wenn du wirklich Item-übergreifende Analytics brauchst — ein GROUP BY, ein JOIN,
ein Aggregat, das DynamoDB nicht ausdrücken kann — führt DynoTables SQL Workbench
sie clientseitig über eine begrenzte Ergebnismenge aus, statt die Tabelle mit einem
vollen Scan zu hämmern.
Nächste Schritte
Schätze, was jedes Muster kostet, mit dem Capacity-Rechner, lies Query vs. Scan für den Kontrast auf API-Ebene und lade DynoTable herunter, um diese gegen deine eigenen Tabellen auszuführen und die verbrauchte Kapazität selbst zu sehen.