Einsteiger6 Min. Lesezeit

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 Scan liest die ganze Tabelle, jedes Mal. Die Größe, nicht deine Ergebnis-Anzahl, entscheidet, was du zahlst und wie lange es dauert.
  • Die FilterExpression ist 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 Scan wird langsamer, während du wächst. Eine gekeyte Query bleibt 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, payloadBytes

Sagen 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 + FilterGekeyte Query
ReadsJedes Item in der TabelleEine Partition, per SK eingeengt
Abgerechnete KapazitätGanze Tabelle, vor dem FilterNur die Items in deiner Scheibe
Unser Beispiel~250.000 RCUs (~500 MB)ein paar hundert RCUs (~2 MB)
LatenzWächst mit der TabellengrößeFlach, während die Tabelle wächst
Ergebnis-AnzahlEntscheidet nichts über die KostenEntspricht 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.

JaNeinJaNeinItems lesen müssenPartition Key bekannt?Query eine PartitionKann ein GSI ihn keyen?Einen GSI hinzufügen, dannQueryScan letzter Ausweg

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.

Aktualisiert