DynamoDB-Key-Condition-Expressions
Eine Key Condition Expression ist die KeyConditionExpression, die du an
eine Query übergibst — der einzige Teil des Requests, den DynamoDB nutzt, um
Items zu finden. Alles andere (Filter, Projektionen) läuft, nachdem der Read
schon metered ist.
Was ist eine Key Condition Expression in DynamoDB?
Eine Key Condition Expression ist die KeyConditionExpression auf einer Query,
die DynamoDB sagt, welche Items zu lesen sind. Der
muss eine Equality sein (PK = :v); der nimmt einen Range-
Operator — =, <, <=, >, >=, BETWEEN oder begins_with. Sie entscheidet,
was gelesen und abgerechnet wird — anders als ein Filter.
- Der muss eine Equality sein.
PK = :vund nichts sonst — keine Ranges, keinbegins_with, keinIN. DynamoDB hasht ihn, um eine Partition zu lokalisieren. - Der nimmt einen Range-Operator.
=,<,<=,>,>=,BETWEENoderbegins_with— hier slicest du eine . - Sie ist kein Filter. Eine Key Condition entscheidet, was gelesen und
abgerechnet wird; eine
FilterExpressiontrimmt nur das Result, nachdem du für den Read bezahlt hast. - Sort Keys sind byte-geordnet. Range-Operatoren vergleichen lexikografisch, also ist die Formatierung des Sort-Key-Strings deine Query-Power.
Warum der Partition Key auf Equality festgenagelt ist
DynamoDB speichert Items, indem es den Partition Key auf eine physische Partition hasht. Ein Hash gibt dir eine Location, keine Range — es gibt also nichts, über das gescannt werden könnte.
Deshalb werden PK > :v oder begins_with(PK, :v) direkt abgelehnt. Die Engine
kann „alle Partitionen, deren Key mit X beginnt“ nicht beantworten, ohne die
ganze Tabelle zu lesen — genau der Scan, den sie vermeiden soll.
Aus SQL fühlt sich das rückwärts an: WHERE id LIKE 'order%' ist in Postgres
trivial. In DynamoDB ist der Partition Key eine Adresse, keine suchbare Spalte.
Der Sort Key ist, wo die Power lebt
Innerhalb einer Partition sind Items nach dem Sort Key sortiert gespeichert. Diese Ordnung nutzen Range-Operatoren — DynamoDB seekt zu einer Position und liest vorwärts.
| Operator | Liest | Nutzen für |
|---|---|---|
SK = :v | Ein exaktes Item | Ein spezifisches Child per Key |
SK < / <= / > / >= :v | Ein offenes Slice | „Alles nach diesem Punkt“ |
SK BETWEEN :a AND :b | Eine geschlossene Range (inkl.) | Ein begrenztes Fenster — Datumsrange |
begins_with(SK, :p) | Ein Prefix-Slice | Ein Typ oder eine Hierarchie unter dem PK |
Es gibt kein LIKE, kein CONTAINS, kein ENDS_WITH auf dem Key. Substring-
und Suffix-Matching sind nicht byte-geordnet, also würden sie einen Full Read
erzwingen — by Design lässt die API das nicht zu. Substring-Matching existiert
über contains() in einer FilterExpression (wo du den Read schon bezahlt hast);
Suffix-Matching ist serverseitig gar nicht verfügbar — speichere einen reversed
Key oder filter clientseitig.
(AWS: Key condition expressions)
Praxisbeispiel: Messages in einer Chat-App
Angenommen, du baust Channel-basierten Chat. Eine Tabelle, partitioniert nach Channel, sortiert nach Message-Zeit. Originales Key Schema:
- Partition Key
ChannelRef—CH#{channelId} - Sort Key
PostedAt— ein ISO-8601-Timestamp,MSG#2026-06-23T14:05:00Z
Das MSG#-Prefix hält Message-Rows sortierbar und unterscheidbar von jedem
anderen Row-Typ, den du unter demselben Channel co-lokalisieren könntest (pinned
Config, Membership).
Die neuesten Messages eines Channels laden. Nur der Partition Key, neueste zuerst:
KeyConditionExpression ChannelRef = :ch
ExpressionAttributeValues { ":ch": "CH#general" }
ScanIndexForward false
ScanIndexForward: false läuft die sortierte Collection rückwärts — der günstige
Weg zu „most recent first“ ohne clientseitiges Sortieren.
Ein bestimmter Tag mit begins_with. Weil der Timestamp der Sort Key ist und
als Text gespeichert wird, ist ein Datums-Prefix ein sauberer Slice:
KeyConditionExpression ChannelRef = :ch AND begins_with(PostedAt, :day)
:ch "CH#general"
:day "MSG#2026-06-23"
Das liest jede Message am 2026-06-23 und nichts sonst — DynamoDB seekt zum Prefix und stoppt, wenn es vom Ende fällt. Das funktioniert nur, weil das Prefix ein echter Left-Anchor eines byte-sortierten Strings ist.
Ein präzises Fenster mit BETWEEN. Für „die Messages während der 14:00-Stunde“
schlägt eine inklusive Range ein Prefix:
KeyConditionExpression ChannelRef = :ch AND PostedAt BETWEEN :lo AND :hi
:ch "CH#general"
:lo "MSG#2026-06-23T14:00:00Z"
:hi "MSG#2026-06-23T14:59:59Z"
BETWEEN ist auf beiden Bounds inklusiv, also wähle Endpoints bewusst — ein
Off-by-One droppt oder verdoppelt hier still eine Edge-Message.
Du kannst jede dieser Expressions zusammenbauen und kopieren, mit gefüllter
ExpressionAttributeValues-Map, im
DynamoDB Expression Builder — praktisch, um
begins_with- und BETWEEN-Syntax beim ersten Mal richtig zu bekommen.
Dieser Builder ist auf eine pk = … AND begins_with(sk, …)-Query voreingestellt —
ändere den Operator, um die KeyConditionExpression zu updaten:
In DynoTable ansehen
Fahre dieselbe Key Condition gegen eine echte Channel-Partition. Sobald du einen
Partition-Key-Filter setzt, stellt DynoTable eine Query — du lädst also nur
diesen Slice, nicht die ganze Collection.
Die Falle: Key Condition mit Filter verwechseln
Der teure Fehler ist, nach FilterExpression zu greifen, um den Job eines Keys
zu machen. Ein Filter darf PostedAt gar nicht referenzieren — es ist der Sort
Key, und DynamoDB lehnt einen Filter auf einem Key-Attribut mit
ValidationException ab. Der Workaround ist also, das Datum in ein plaines Non-
Key-Attribut (MessageDate) zu duplizieren und darauf zu filtern:
KeyConditionExpression ChannelRef = :ch
FilterExpression begins_with(MessageDate, :day)
Das sieht äquivalent zur begins_with-Key-Condition oben aus und liefert
dieselben Rows — aber es liest zuerst die gesamte Channel-Partition und
verwirft dann alles außerhalb des Tages. Du wirst für den Full Read abgerechnet.
Filter reduzieren nie Read-Kosten. Sie laufen, nachdem DynamoDB die Items
gemetered hat — derselbe Fallstrick wie ein gefilterter
Scan. Wenn ein Prädikat in die Key Condition
kann, gehört es dahin.
Fix es upstream. Wenn ein Access Pattern nicht als eine PK-Equality plus eine Sort-Key-Range ausdrückbar ist, ist das ein Modeling-Signal. Reshape den Sort Key, oder füge einen Index hinzu, der für das Pattern gekeyed ist — siehe GSI vs LSI und Single-Table Design für die Key-Layout.
Fallstricke und nächste Schritte
- Partition Key ist immer
=. Keine Ranges, jemals. Brauchst du eine Range über Partitionen, bist du über eine einzelneQueryhinausgewachsen. - Eine Sort-Key-Condition pro Query. Du kannst nicht zwei Sort-Key-Prädikate
ANDen; wähleBETWEENoderbegins_with, nicht beides. - Reserved Words brauchen Aliase. Ein Key namens
TimestampoderNamemussExpressionAttributeNames(#ts) nutzen, sonst errort die Query. (AWS: reserved words) BETWEENist inklusiv. Beide Endpoints matchen — designe deine Bounds entsprechend.
Draft deine Key Conditions im Expression Builder, dann probier DynoTable, um sie gegen deine eigenen Tabellen zu fahren und genau zu sehen, welchen Slice jede Key Condition zurückgibt.