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, nach der du aus SQL-Muskelgedächtnis greifst — und die, die still deine Rechnung hochtreibt, während deine Latenz schlechter wird 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, also zahlst du, die ganze Tabelle zu lesen, egal wie wenige Rows zurückkommen — und er wird langsamer, wenn die Tabelle wächst. Der Fix ist fast immer eine keyed Query: modelliere das Access Pattern um einen Key, sodass DynamoDB eine Partition berührt statt alles.

  • Ein Scan liest die ganze Tabelle, jedes Mal. Größe, nicht deine Result-Count, entscheidet, was du zahlst und wie lange es dauert.
  • Die FilterExpression lügt über Kosten. Sie läuft nachdem der Read metered ist, also kann das Zurückgeben von 12 Items das Lesen von 12 Millionen abrechnen.
  • Ein Scan wird langsamer, wenn du wächst. Eine keyed Query bleibt flach — sie berührt eine Partition, egal wie groß die Tabelle wird.
  • Der Fix ist fast immer Modeling, nicht Tuning. Wenn du Scanst, um eine Routinefrage zu beantworten, fehlt dir ein Key.

Was ein Scan wirklich tut

Aus SQL fühlt sich SELECT * FROM events WHERE type = 'checkout' gratis an — die Engine hat einen Index oder nicht, aber so oder so bekommst du Rows zurück. In DynamoDB entscheidet kein Query Planner das für dich.

Ein Scan läuft die gesamte Tabelle sequenziell ab, 1 MB pro Seite, und reicht jede Seite an deine FilterExpression. Was der Filter verwirft, ist trotzdem gelesen, trotzdem metered und trotzdem auf deiner Rechnung. (AWS: Scanning tables)

Das ist die Falle. Der Filter sieht aus wie eine WHERE-Klausel, ändert aber das Result Set, nie die Kosten. Ein Scan verbraucht dieselbe Read Capacity, ob ein Filter da ist oder nicht. (AWS: Scanning tables)

Die Read Units zählen

DynamoDB metert Reads in (RCUs). Eine RCU kauft einen einzelnen Read eines Items bis 4 KB; Reads kosten die Hälfte. Größere Items runden auf die nächsten 4 KB auf. (AWS: Read/write capacity mode)

Nimm eine Analytics-Tabelle ProductEvents. Jede Row ist ein getracktes Event:

PK  = "TENANT#acme"
SK  = "TS#2026-06-23T14:08:55Z#evt_9f3a"
attrs: eventType, sessionId, userId, payloadBytes

Angenommen, sie hält 2.000.000 Events à ~1 KB, alle unter einem busy Tenant. Du willst die Checkouts von heute. Der reflexhafte Move:

Scan ProductEvents
FilterExpression: eventType = "checkout"

Dieser Filter liefert vielleicht 40 Rows. Aber der Scan hat zuerst alle 2.000.000 Items gelesen. Bei ~1 KB je (1 RCU pro 4 KB, eventually consistent ≈ 0,5 RCU pro 4 KB) hast du grob 250.000 RCUs metered — und ~2 GB Daten gepaged — um 40 Items zurückzugeben.

Jetzt modelliere das Access Pattern als Key und Query es:

Query ProductEvents
PK = "TENANT#acme"
AND SK begins_with "TS#2026-06-23"

Das liest nur den gematchten Slice einer Partition. Wenn diese 40 Checkout-Rows plus die anderen Events des Tages ~2 MB ergeben, zahlst du für ~2 MB Reads, nicht 2 GB. Dieselbe Antwort, ein winziger Bruchteil der Kosten — und die Latenz bleibt flach, wenn die Tabelle wächst.

Scan vs Query, metered

Scan + FilterKeyed Query
ReadsJedes Item in der TabelleEine Partition, per SK verengt
Abgerechnete CapacityGanze Tabelle, vor dem FilterNur die Items in deinem Slice
Unser Beispiel~250.000 RCUs (~2 GB)ein paar hundert RCUs (~2 MB)
LatenzWächst mit TabellengrößeFlach, wenn die Tabelle wächst
Result countEntscheidet nichts über KostenPasst zu dem, wofür du zahlst

Bei einem Scan sind Result count und Rechnung unverbunden. Bei einer Query tracken sie einander.

Entscheide, bevor du scannst

Die meisten versehentlichen Scans kommen von einer Frage: kann ich die Partition nennen, die ich brauche? Wenn ja, ist es eine Query. Wenn nein, ist der Fix ein Key, kein größerer Filter.

Hier die Entscheidung als Flow.

JaNeinJaNeinItems lesen müssenPartition Key bekannt?Query eine PartitionKann ein GSI es keyen?GSI hinzufügen, dann QueryScan letzte Option

Der Pfad endet fast immer bei Query; du fällst nur zu Scan durch, wenn kein Key — vorhanden oder hinzufügbar — zum Access Pattern passt.

Wenn das Pattern real und wiederkehrend ist, die Base Table es aber nicht keyen kann, ist das das Signal, einen Global Secondary Index hinzuzufügen, damit die Frage eine Query wird. Keys um Access Patterns zu modellieren ist das ganze Spiel — siehe Single-Table Design.

Schreib die keyed Query, nicht einen Filter

Wenn du eine Condition jenseits des Keys brauchst, baue sie bewusst, statt alles in eine FilterExpression zu kippen. Der DynamoDB Expression Builder generiert KeyConditionExpression und Attribut-Placeholder für dich, sodass Partition und Sort Key die Verengung machen — bevor DynamoDB den Read metert, nicht danach.

KeyConditionExpression: PK = :tenant AND begins_with(SK, :day)

Wann ein Scan wirklich fine ist

Ein Scan ist der falsche Default für Routine-Queries. Er ist das richtige Tool, wenn du wirklich „alles lesen“ meinst:

  • One-off Exports oder Backfills von Hand.
  • Winzige Config-/Lookup-Tabellen, wo die ganze Tabelle ein paar KB ist.
  • Background Jobs, die die volle Tabelle absichtlich pagen. Splittet die über Worker mit Segment / TotalSegments — ein — statt eines langen sequenziellen Crawls. (AWS: Scanning tables)

Und beachte: PartiQL rettet dich nicht. SELECT * FROM ProductEvents WHERE eventType = 'checkout' ohne Key-Prädikat kompiliert geradewegs zu einem Scan. Derselbe Fallstrick in SQL-Kleidung. (Siehe Query vs Scan für den vollen Breakdown.)

Wenn du wirklich Cross-Item-Analytics brauchst — ein GROUP BY, ein JOIN, ein Aggregat, das DynamoDB nicht ausdrücken kann — führt DynoTables SQL Workbench sie clientseitig über ein begrenztes Resultset aus, statt die Tabelle mit einem Full Scan zu hämmern.

Nächste Schritte

Schätze, was jedes Pattern kostet, mit dem Pricing Calculator, lies Query vs Scan für den API-Level-Kontrast, und lade DynoTable herunter, um das gegen deine eigenen Tabellen zu fahren und zu sehen, wie viele Items jeder Ansatz wirklich liest.

Aktualisiert