Einsteiger5 Min. Lesezeit

Query vs Scan in DynamoDB

Query liest eine einzelne Item-Collection per Partition Key (optional verengt über den Sort Key); Scan liest die ganze Tabelle und filtert hinterher. In der API sehen sie ähnlich aus, aber sie berechnen — und skalieren — völlig unterschiedlich.

Wann sollte ich Query vs. Scan in DynamoDB verwenden?

Verwende Query, wann immer du die benötigte Partition benennen kannst — er liest eine einzelne Item-Collection und berechnet nur für gematchte Items. Greife auf Scan nur für einmalige Exporte oder winzige Tabellen zurück; er liest jedes Item und berechnet die gesamte Tabelle, bevor eine FilterExpression läuft. Bei echten Daten gewinnt Query.

  • Query ist gezielt: du zahlst für die Items in der getroffenen Partition.
  • Scan ist erschöpfend: du zahlst, um jedes Item zu lesen, und wirfst das meiste dann mit einer FilterExpression weg, die nach dem berechneten Lesen läuft.

Auf einer Tabelle echter Größe ist ein Scan mit Filter die klassische „Warum ist meine Rechnung riesig und meine Latenz schlechter als bei RDS“-Fußangel.

Gegenüberstellung

QueryScan
LiestEine Partition (per PK)Jedes Item in der Tabelle
Berechnete KapazitätIn der Partition getroffene ItemsGanze Tabelle, vor dem Filtern
FilterExpressionNach dem Lesen angewandt — Lesen wird trotzdem berechnetDasselbe — Filtern senkt nie Kosten
LatenzFlach, während die Tabelle wächstWächst mit der Tabellengröße
Pagination1 MB/Seite → LastEvaluatedKey1 MB/Seite; parallelisierbar
Verwende es fürBekannte ZugriffsmusterEinmalige Exporte, winzige Config-Tabellen

Die zentrale Falle: eine FilterExpression läuft nach dem berechneten Lesen durch DynamoDB, bei beiden Operationen. Ein Scan, der „10 Zeilen zurückgibt“, kann für das Lesen von einer Million berechnen — Filtern ist eine Bequemlichkeit, nie eine Kostenkontrolle.

Was ein voller Scan tatsächlich kostet

Machen wir es konkret. DynamoDB berechnet Lesevorgänge in 4-KB-Einheiten: ein Lesevorgang kostet eine pro 4 KB, ein Lesevorgang die Hälfte davon. Query und Scan summieren die Größe jedes Items, das sie anfassen — nicht jedes Items, das sie zurückgeben — und runden auf die nächsten 4 KB auf.

Nimm eine Tabelle mit 1 Million Items und durchschnittlich 2 KB pro Item (~2 GB an Daten) und ein Zugriffsmuster, das 10 dieser Items braucht:

Gelesene ItemsBerechnete DatenRead Units (letztendlich konsistent)
Scan + FilterExpression1,000,000~2 GB~262,000
Query auf einem passenden Schlüssel1020 KB3

Dieselben 10 Items, fünf Größenordnungen auseinander — und der Scan berechnet diese ~262,000 bei jedem Lauf, egal ob der Filter zehn Items trifft oder keins. Bei -Abrechnung landen diese Request Units direkt auf der Rechnung; auf Tabellen konkurriert ein großer Scan mit dem Produktions-Traffic um Durchsatz und kann ihn in eine ProvisionedThroughputExceededException drosseln.

Drei weitere Kosten-Fakten, die viele überraschen:

  • Select: COUNT ist nicht gratis. Ein zählender Query oder Scan verbraucht exakt dieselbe Lesekapazität wie das Lesen der Items — er gibt sie nur nicht zurück.
  • Limit begrenzt die ausgewerteten Items, nicht die getroffenen. Kombiniert mit einem Filter kann eine Seite leer zurückkommen und trotzdem eine volle Seite an Lesevorgängen berechnen.
  • Du musst nie raten. Übergib ReturnConsumedCapacity: TOTAL, und jede Antwort meldet die Kapazität, die sie gerade verbraucht hat.

Prüfe mit dem Item-Größen-Rechner, wie viel deine eigenen Items wiegen, und rechne dann mit dem Preisrechner Read Units in eine Monatsrechnung um.

Query verwenden

Query  PK = "USER#42"  AND  SK begins_with "ORDER#"

Wenn du zu Scan greifst, um ein häufiges Zugriffsmuster zu beantworten, ist das ein Modellierungssignal: füge einen Global Secondary Index hinzu, damit das Muster zu einem Query wird.

Die Entscheidung läuft auf eine Frage hinaus — kannst du die Partition benennen, die du brauchst?

YesNoYesNoAccess patternPartition key known?Query reads one partitionCan a GSI key it?Add a GSIScan reads the whole table

Ist der Schlüssel bekannt, machst du ein Query; wenn nicht, füge einen GSI hinzu, um eines daraus zu machen, und greife nur dann auf Scan zurück, wenn kein Schlüssel passt.

Wann Scan in Ordnung ist

Einmalige Exporte, winzige Config-Tabellen und Hintergrundjobs, die bewusst durch die ganze Tabelle blättern. Verwende Segment/TotalSegments, um einen Scan über Worker aufzuteilen (einen — siehe parallele Scans in DynamoDB), wenn du wirklich alles lesen musst, und paginiere ihn sauber mit LastEvaluatedKey (Paginierung). Wenn ein Scan, den du bereits fährst, das Problem ist, führt dich warum Scan langsam und teuer ist durch die Triage.

Ein reflexartiges SELECT * FROM table über DynamoDB ist dasselbe Anti-Pattern im PartiQL-Gewand — es kompiliert zu einem Scan. Wenn du wirklich itemübergreifende Analytik brauchst (ein GROUP BY, ein JOIN, ein Aggregat), führt DynoTables SQL Workbench sie clientseitig über eine begrenzte Ergebnismenge aus, statt die Tabelle zu hämmern.

Probiere DynoTable, um diese Abfragen gegen deine eigenen Tabellen auszuführen und zu inspizieren — es zeigt die verbrauchte Kapazität jeder Operation, die es ausführt.

Aktualisiert