DynamoDB の条件式
条件式は、DynamoDB が書き込みをコミットする 前 に、既存のアイテムに対して評価する
述語です。述語が偽なら、書き込みは拒否され、何も変わりません。それは DynamoDB が持つ、
書き込みに対する WHERE 句に最も近いもの — そして不変条件を強制する唯一の安全な方法です。
DynamoDB の条件式はどのように機能するか?
条件式は、書き込みをコミットする前に、DynamoDB がサーバー側で現在のアイテムに対して評価する述語です。真であれば書き込みは進み、偽であれば書き込みは ConditionalCheckFailedException で拒否され、何も変わりません。チェックと変更を1つのアトミック操作に畳み込むため、同時実行の呼び出し元が古い読み取りで競合することはありません。
- それはフィルタではなくガード。
ConditionExpressionは現在のアイテムに対して サーバー側で動き、偽の結果はConditionalCheckFailedExceptionで書き込みを失敗させます。 - それは read-then-write を置き換える。
SELECTしてからUPDATEするラウンド トリップなし — チェックと変更が1つのアトミック操作なので、2人の呼び出し元が競合できません。 - 拒否は無料だが、実行は無料ではない。 失敗した条件付き書き込みでも書き込みキャパシティ を消費します。その保証は、それがブロックする書き込みと同じだけのコストです。
SQL から来ると、行を読み、アプリのコードでチェックし、それから更新するでしょう。 DynamoDB では、その読み取りと書き込みの間のギャップが、同時実行の呼び出し元を待ち受ける データ破損のバグです。条件式がそのギャップを閉じます。
どこに適用されるか
ConditionExpression は PutItem、UpdateItem、DeleteItem、そして
TransactWriteItems 内の各アクションに付けられます。Query や Scan の一部では
ありません — それらは読み取り経路上の別物である FilterExpression を使います。
その区別が人々を混乱させるので、正確に言いましょう。
ConditionExpression | FilterExpression | |
|---|---|---|
| 経路 | 書き込み(Put/Update/Delete) | 読み取り(Query/Scan) |
| 失敗時の効果 | 書き込み全体を拒否 | アイテムを結果から落とす |
| 見るもの | 現在のアイテム、書き込み前 | 各候補アイテム、読み取り後 |
| コスト | 失敗した書き込みも課金される | フィルタされたアイテムも読み取り分は課金される |
どちらもサーバー側で動きます。違いは「偽」が何をするかです。条件は変更を中止し、フィルタは すでに読み取り分を支払った行を隠すだけです。 (AWS: Condition Expressions)
実際に使う関数
条件言語は小さいです。働き者たちは。
attribute_exists(path)/attribute_not_exists(path)— この属性はアイテムに存在 するか? 「なければ作成のみ」/「あれば更新のみ」の古典的なイディオムです。- 比較演算子 —
=、<>、<、<=、>、>=— 値や別の属性に対して。 attribute_type、begins_with、contains、size— 型と文字列/セットのチェック。BETWEEN … AND …、IN (…)— 範囲とメンバーシップ。AND、OR、NOT、括弧 — 上記を組み合わせるため。
パーティションキーに対する attribute_not_exists は、既存のアイテムを潰さない挿入のように
PutItem を振る舞わせる定石です — DynamoDB には別個の「挿入」操作がないため、条件 が
挿入のセマンティクスそのものです。
(AWS: Comparison Operator and Function Reference)
具体例:残高不足から元帳を守る
銀行の元帳を取り上げます。各口座は1つのアイテムです。
PK = "ACCT#a7f3"
SK = "BALANCE"
clearedCents = 50000
holdCents = 0不変条件:引き落としは利用可能残高を決してゼロ未満に押し下げてはならず、存在しない口座を 決して引き落としてはなりません。2つのルール、どちらも書き込みそのものの中で強制できます。
間違ったやり方(落とし穴)
GetItem ACCT#a7f3 / BALANCE → clearedCents = 50000
if (50000 >= 30000) ... ← アプリ側のチェック
UpdateItem SET clearedCents = 20000
GetItem と UpdateItem の間で、2つ目の引き落としが同じ 50000 を読み、自身のチェックを
通過し、書き込みできます。両方が成功し、口座はマイナスになります。これは read-modify-write
の競合であり、どれだけアプリ側で検証しても直りません — チェックと書き込みが別々の操作
だからです。
正しいやり方
チェックを書き込みに畳み込みます。口座が存在し かつ 十分に保持していることを条件に、 30000 セントを引き落とします。
UpdateItem ACCT#a7f3 / BALANCE
SET clearedCents = clearedCents - :amt
ConditionExpression:
attribute_exists(PK) AND clearedCents >= :amt:amt = 30000 で。残高が低すぎるか、アイテムが作成されていなければ、DynamoDB は
ConditionalCheckFailedException で書き込みを拒否し、残高は手つかずです。同時実行の
引き落としは、元の残高を見てそれに対してチェックされるか、更新後の残高を見るかのどちらかで
— 作用した古い読み取りには決してなりません。
ExpressionAttributeValues マップを手組みする代わりに、その正確な式 — 名前も値も
すべて — を DynamoDB Expression Builder で構築して
コピーできます。
DynoTable でガードを検査する
条件付き書き込みが失敗したとき、推測するのではなくアイテムの本当の状態を見たいものです。
口座アイテムを呼び出して clearedCents を直接読みましょう。

拒否を読み、闇雲にリトライしない
ConditionalCheckFailedException は一時的なエラーではありません — 同じ書き込みを
リトライしても何も変わりません。それはビジネスルールが発火したことを意味します。残高不足、
重複作成、古いバージョン。インフラの一過性の不調としてではなく、ドメインの結果として
表面化させましょう。
2つのことが失敗をデバッグ可能にします。
ReturnValuesOnConditionCheckFailure: ALL_OLD— DynamoDB が失敗とともに現在の アイテムを返すので、2回目の読み取りなしに「残高は 20000 でしたが、30000 を要求しました」 と表示できます。 (AWS: Working with Items)- 2つの失敗理由を区別する。
attribute_exists(PK) AND clearedCents >= :amtは 「口座なし」と「資金なし」を1つの例外に畳み込みます。呼び出し元がそれらを区別する必要が あるなら、2つの書き込みに分けるか、返されたアイテムを検査します。
楽観的ロックも同じトリック
バージョン番号パターンは、別の帽子をかぶった条件式にすぎません。version 属性を保存し、
すべての書き込みが、読んだバージョンをアサートしてそれをインクリメントします。
UpdateItem ACCT#a7f3 / BALANCE
SET clearedCents = :new, version = :next
ConditionExpression: version = :seen別の書き手が先に動いたなら、version = :seen は偽で、書き込みは拒否され、あなたは再読
してリトライします。これが、ロックなしで DynamoDB が同時実行制御を行う方法です — 見た
ものをアサートし、動いていたら失敗する。(AWS: Optimistic Locking with Version Number)
落とし穴と次のステップ
- 予約語と衝突する名前。
status、size、name、そして他およそ570語が予約されて います。ExpressionAttributeNames(#s = status)でエイリアスを付けないと、式が静かに パースに失敗します。 - 条件は別のアイテムを参照できない。 書き込まれているアイテムだけを見ます。アイテムを
またぐ不変条件には、アクションごとの
ConditionExpressionを持つTransactWriteItemsか、番兵アイテムに対するConditionCheckが必要です。 - 失敗した書き込みも WCU を消費する。 90% の確率で拒否するガードでも、それらの拒否分を 課金されます。安い保険ですが、無料ではありません。
これらのガードが対象とするキーのモデリングについては、 シングルテーブル設計 と Query と Scan を参照してください。実データに対して条件付き書き込みを発行する準備ができたら、 DynoTable をダウンロード して、自分のテーブルに対してそれらを実行しましょう。


