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
FilterExpressionweg, 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
| Query | Scan | |
|---|---|---|
| Liest | Eine Partition (per PK) | Jedes Item in der Tabelle |
| Berechnete Kapazität | In der Partition getroffene Items | Ganze Tabelle, vor dem Filtern |
FilterExpression | Nach dem Lesen angewandt — Lesen wird trotzdem berechnet | Dasselbe — Filtern senkt nie Kosten |
| Latenz | Flach, während die Tabelle wächst | Wächst mit der Tabellengröße |
| Pagination | 1 MB/Seite → LastEvaluatedKey | 1 MB/Seite; parallelisierbar |
| Verwende es für | Bekannte Zugriffsmuster | Einmalige 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 Items | Berechnete Daten | Read Units (letztendlich konsistent) | |
|---|---|---|---|
Scan + FilterExpression | 1,000,000 | ~2 GB | ~262,000 |
Query auf einem passenden Schlüssel | 10 | 20 KB | 3 |
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: COUNTist 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.Limitbegrenzt 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?
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.