DynamoDB Condition Expressions: Der vollständige Leitfaden (mit Beispielen)
Eine Condition Expression ist ein Prädikat, das DynamoDB am bestehenden Item
vor dem Commit deines Schreibvorgangs auswertet. Ist das Prädikat falsch, wird der Schreibvorgang
abgelehnt und nichts ändert sich. Es ist das, was DynamoDB einer
WHERE-Klausel bei einem Schreibvorgang am nächsten kommt — und die einzig sichere Art, eine Invariante zu erzwingen.
Wie funktionieren Condition Expressions in DynamoDB?
Eine Condition Expression ist ein Prädikat, das DynamoDB serverseitig gegen das aktuelle Item auswertet, bevor ein Schreibvorgang committet wird. Ist es wahr, geht der Schreibvorgang durch; ist es falsch, wird der Schreibvorgang mit ConditionalCheckFailedException abgelehnt und nichts ändert sich. Es faltet die Prüfung und die Mutation in eine einzige atomare Operation, sodass gleichzeitige Aufrufer nicht gegen einen veralteten Lesevorgang rennen können.
- Es ist ein Wächter, kein Filter.
ConditionExpressionläuft serverseitig am aktuellen Item; ein falsches Ergebnis lässt den Schreibvorgang mitConditionalCheckFailedExceptionscheitern. - Es ersetzt Read-then-Write. Keine
SELECT-dann-UPDATE-Runde — die Prüfung und die Mutation sind eine atomare Operation, sodass zwei Aufrufer nicht rennen können. - Ablehnen ist kostenlos, Ausführen nicht. Ein fehlgeschlagener bedingter Schreibvorgang verbraucht trotzdem Schreibkapazität. Ein abgelehnter Schreibvorgang berechnet WCUs für die Größe des bestehenden Items, gegen das er geprüft wurde (mindestens 1) — ein fehlgeschlagenes Create-if-absent kostet 1 WCU.
Aus SQL kommend würdest du die Zeile lesen, sie im App-Code prüfen und dann aktualisieren. In DynamoDB ist diese Lücke zwischen Lesen und Schreiben ein Datenkorruptionsbug, der auf einen gleichzeitigen Aufrufer wartet. Die Condition Expression schließt die Lücke.
Wo sie greifen
Du hängst eine ConditionExpression an PutItem, UpdateItem, DeleteItem und
jede Aktion innerhalb von TransactWriteItems. Sie ist kein Teil von Query oder Scan
— die verwenden FilterExpression, was auf dem Lesepfad etwas anderes ist.
Diese Unterscheidung bringt Leute durcheinander, also sei genau:
ConditionExpression | FilterExpression | |
|---|---|---|
| Pfad | Schreibvorgänge (Put/Update/Delete) | Lesevorgänge (Query/Scan) |
| Effekt bei Fehlschlag | Lehnt den ganzen Schreibvorgang ab | Entfernt das Item aus den Ergebnissen |
| Sieht | Das aktuelle Item, vor dem Schreiben | Jedes Kandidaten-Item, nach dem Lesen |
| Kosten | Fehlgeschlagener Schreibvorgang wird trotzdem berechnet | Gefilterte Items werden trotzdem für den Lesevorgang berechnet |
Beide laufen serverseitig. Der Unterschied ist, was „falsch" bewirkt: eine Condition bricht eine Mutation ab; ein Filter verbirgt nur eine Zeile, für deren Lesen du bereits bezahlt hast. (AWS: Condition Expressions)
Die Funktionen, die du tatsächlich verwenden wirst
Die Condition-Sprache ist klein. Die Arbeitspferde:
attribute_exists(path)/attribute_not_exists(path)— existiert dieses am Item? Die klassische Redewendung für „nur erstellen, wenn nicht vorhanden" / „nur aktualisieren, wenn vorhanden".- Vergleichsoperatoren —
=,<>,<,<=,>,>=— gegen einen Wert oder ein anderes Attribut. attribute_type,begins_with,contains,size— Typ- und String-/Set-Prüfungen.BETWEEN … AND …,IN (…)— Bereich und Zugehörigkeit.AND,OR,NOT, Klammern — um die obigen zu kombinieren.
attribute_not_exists auf dem ist die kanonische Art, PutItem
sich wie ein Insert verhalten zu lassen, das ein bestehendes Item nicht überschreibt — DynamoDB
hat keine separate „Insert"-Operation, also ist die Condition die Insert-Semantik.
(AWS: Comparison Operator and Function Reference)
Ein durchgearbeitetes Beispiel: ein Kassenbuch gegen Überziehung absichern
Nimm ein Bank-Kassenbuch. Jedes Konto ist ein Item:
PK = "ACCT#a7f3"
SK = "BALANCE"
clearedCents = 50000
holdCents = 0Eine Belastung darf den verfügbaren Saldo niemals unter null drücken, und du darfst niemals ein Konto belasten, das nicht existiert. Zwei Regeln, beide im Schreibvorgang selbst durchsetzbar.
Der falsche Weg (der Fallstrick)
GetItem ACCT#a7f3 / BALANCE → clearedCents = 50000
if (50000 >= 30000) ... ← app-side check
UpdateItem SET clearedCents = 20000
Zwischen dem GetItem und dem UpdateItem kann eine zweite Belastung dieselben
50000 lesen, ihre eigene Prüfung bestehen und ebenfalls schreiben. Beide gelingen; das Konto wird
negativ. Das ist ein Read-Modify-Write-Race, und keine noch so gute App-seitige Validierung
behebt es — die Prüfung und der Schreibvorgang sind getrennte Operationen.
Der richtige Weg
Falte die Prüfung in den Schreibvorgang. Belaste 30000 Cent, bedingt darauf, dass das Konto existiert und genug hält:
UpdateItem ACCT#a7f3 / BALANCE
SET clearedCents = clearedCents - :amt
ConditionExpression:
attribute_exists(PK) AND clearedCents >= :amtmit :amt = 30000. Ist der Saldo zu niedrig oder wurde das Item nie erstellt,
lehnt DynamoDB den Schreibvorgang mit
ConditionalCheckFailedException
ab und der Saldo bleibt unangetastet. Die gleichzeitige Belastung sieht entweder den ursprünglichen Saldo und wird
gegen ihn geprüft, oder sie sieht den aktualisierten — nie einen veralteten Lesevorgang, auf dem sie handelte.
Du kannst den exakten Ausdruck — Namen, Werte und alles — mit dem
DynamoDB Expression Builder bauen und kopieren, statt
die ExpressionAttributeValues-Map von Hand zusammenzusetzen.
Probier es gleich hier aus — dieser Builder ist auf ein abgesichertes PutItem
(attribute_not_exists) voreingestellt, sodass du die generierte ConditionExpression lesen kannst:
Den Wächter in DynoTable inspizieren
Wenn ein bedingter Schreibvorgang fehlschlägt, willst du den realen Zustand des Items sehen, nicht ihn
raten. Rufe das Konto-Item auf und lies clearedCents direkt aus.

Lies die Ablehnung, wiederhole nicht blind
ConditionalCheckFailedException ist kein vorübergehender Fehler — denselben
Schreibvorgang zu wiederholen ändert nichts. Es bedeutet, dass eine Geschäftsregel gefeuert hat: unzureichende Deckung,
doppelte Erstellung, veraltete Version. Behandle es als fachliches Ergebnis, nicht als Infrastruktur-
Aussetzer.
Zwei Dinge machen Fehlschläge debugbar:
ReturnValuesOnConditionCheckFailure: ALL_OLD— DynamoDB gibt das aktuelle Item zusammen mit dem Fehlschlag zurück, sodass du „Saldo war 20000, du hast 30000 verlangt" ohne einen zweiten Lesevorgang anzeigen kannst. (AWS: Working with Items)- Die beiden Fehlschlaggründe unterscheiden.
attribute_exists(PK) AND clearedCents >= :amtfasst „kein Konto" und „keine Deckung" in eine Ausnahme zusammen. Müssen Aufrufer sie auseinanderhalten, teile in zwei Schreibvorgänge auf oder inspiziere das zurückgegebene Item.
Optimistic Locking ist derselbe Trick
Das Versionsnummern-Muster ist nur eine Condition Expression, die einen anderen
Hut trägt. Speichere ein version-Attribut; jeder Schreibvorgang behauptet die Version, die du gelesen hast, und
erhöht sie:
UpdateItem ACCT#a7f3 / BALANCE
SET clearedCents = :new, version = :next
ConditionExpression: version = :seenHat ein anderer Schreiber zuerst gehandelt, ist version = :seen falsch, der Schreibvorgang wird abgelehnt,
und du liest neu und wiederholst. So betreibt DynamoDB Nebenläufigkeitskontrolle ohne
Sperren — behaupte, was du gesehen hast, scheitere, wenn es sich bewegt hat. (AWS: Optimistic Locking with
Version Number)
DynoTables Staging-Bereich führt dieses Muster für dich aus — eine
gleichzeitige Änderung taucht als aufzulösender Konflikt auf, nicht als verlorener Write.
Fallstricke und nächste Schritte
- Namen, die mit reservierten Wörtern kollidieren.
status,size,nameund ~570 weitere sind reserviert. Aliasiere sie mitExpressionAttributeNames(#s = status) oder die Anfrage wird mit einer ValidationException abgelehnt („Attribute name is a reserved keyword"). Der Reserved-Words-Checker nimmt deine Attributnamen entgegen und liefert die Alias-Map fertig zum Einfügen zurück. - Eine Condition kann kein anderes Item referenzieren. Sie sieht nur das gerade
geschriebene Item. Item-übergreifende Invarianten brauchen
TransactWriteItemsmit einerConditionExpressionpro Aktion oder einenConditionCheckgegen ein Sentinel-Item. - Fehlgeschlagene Schreibvorgänge kosten trotzdem WCUs. Ein Wächter, der 90 % der Zeit ablehnt, wird trotzdem für diese Ablehnungen berechnet. Günstige Versicherung, aber nicht kostenlos.
Für das Modellieren der Keys, gegen die diese Wächter laufen, siehe Single-Table-Design und Query vs Scan. Wenn du bereit bist, bedingte Schreibvorgänge gegen echte Daten abzusetzen, lade DynoTable herunter und führe sie gegen deine eigenen Tabellen aus.


