Fortgeschritten5 Min. Lesezeit

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 = :v und nichts sonst — keine Ranges, kein begins_with, kein IN. DynamoDB hasht ihn, um eine Partition zu lokalisieren.
  • Der nimmt einen Range-Operator. =, <, <=, >, >=, BETWEEN oder begins_with — hier slicest du eine .
  • Sie ist kein Filter. Eine Key Condition entscheidet, was gelesen und abgerechnet wird; eine FilterExpression trimmt 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.

OperatorLiestNutzen für
SK = :vEin exaktes ItemEin spezifisches Child per Key
SK < / <= / > / >= :vEin offenes Slice„Alles nach diesem Punkt“
SK BETWEEN :a AND :bEine geschlossene Range (inkl.)Ein begrenztes Fenster — Datumsrange
begins_with(SK, :p)Ein Prefix-SliceEin 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 ChannelRefCH#{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:

Erstelle deine Anfrage
Generierter Code
new QueryCommand({
  "TableName": "AuditLog",
  "KeyConditionExpression": "#hashKey = :hashKeyValue AND begins_with(#rangeKey, :rangeKeyValue)",
  "ExpressionAttributeNames": {
    "#hashKey": "pk",
    "#rangeKey": "sk"
  },
  "ExpressionAttributeValues": {
    ":hashKeyValue": {
      "S": "TENANT#acme"
    },
    ":rangeKeyValue": {
      "S": "EVENT#2026-06"
    }
  }
})

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 einzelne Query hinausgewachsen.
  • Eine Sort-Key-Condition pro Query. Du kannst nicht zwei Sort-Key-Prädikate ANDen; wähle BETWEEN oder begins_with, nicht beides.
  • Reserved Words brauchen Aliase. Ein Key namens Timestamp oder Name muss ExpressionAttributeNames (#ts) nutzen, sonst errort die Query. (AWS: reserved words)
  • BETWEEN ist 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.

Aktualisiert