使用 AWS CLI 執行 DynamoDB 條件寫入

條件寫入從 shell 送出很直接,難的是讀懂它,因為一次失敗真正有趣的結果是以錯誤而非輸出的形式抵達。DynamoDB 條件運算式談的是這個運算式能表達什麼;本頁談的是從 CLI 執行它,並把落敗的那個項目從失敗中取出來。

程式碼

aws dynamodb update-item \
  --table-name 'Music' \
  --key '{"Artist":{"S":"Arturo Sandoval"},"SongTitle":{"S":"Cubano Chant"}}' \
  --update-expression 'SET #upd0 = :updValue0, #version = :newVersion' \
  --condition-expression 'attribute_exists(#cond0) AND #version = :expectedVersion' \
  --expression-attribute-names '{"#upd0":"Genre","#version":"Version","#cond0":"Artist"}' \
  --expression-attribute-values '{":updValue0":{"S":"Latin Jazz"},":expectedVersion":{"N":"7"},":newVersion":{"N":"8"}}'

成功時指令什麼都不會印出,並以 0 結束。如果有另一個寫入者搶先一步,條件就會失敗,CLI 會回報服務端的訊息:

An error occurred (ConditionalCheckFailedException) when calling the UpdateItem operation:
The conditional request failed

說明

  • 成功是靜默的。沒有輸出,結束代碼 0。沒有東西可以解析,也沒有東西可以斷言,所以 shell 指令碼只能把結束狀態當作結果。若你想印出更新後的項目,請加上 --return-values ALL_NEW
  • 失敗的結束狀態是 254,那是 CLI v2 表示用戶端錯誤的代碼,而且與格式錯誤的請求共用同一個代碼。在重試之前先依訊息分支,否則你運算式裡的一個錯字就會變成無限的退避迴圈。
  • --return-values-on-condition-check-failure ALL_OLD 在這裡確實有用。有效值是 ALL_OLDNONE,而且不會消耗讀取容量。要把項目從錯誤裡取出來還需要多一個旗標,下面會談到。
  • 條件與更新是兩個不同的旗標,卻共用同一個命名空間--expression-attribute-names--expression-attribute-values 會跨 --update-expression--condition-expression 合併,這就是為什麼產生出來的名稱是 #upd0#cond0 這樣連號,而不是每個子句各自重新編號。同一個佔位符被拿來表達兩種意思時,第二個會無聲地勝出。
  • 失敗的寫入照樣計費。開發人員指南講得很明白:條件求值為 false 仍然會消耗寫入容量,並以新舊項目中較大的那一個計算大小。條件不是一種便宜的存在性探測。

失敗輸出,以及如何從中取出項目

把那段程式碼跑一次,它會靜默成功。當 Version 不再是 7 時再跑第二次,aws-cli/2.36.9 會對 stderr 印出:

aws: [ERROR]: An error occurred (ConditionalCheckFailedException) when calling the UpdateItem operation: The conditional request failed

加上 --return-values-on-condition-check-failure ALL_OLD,預設輸出會告訴你還有更多內容,但不會秀出來:

aws: [ERROR]: An error occurred (ConditionalCheckFailedException) when calling the UpdateItem operation: The conditional request failed

Additional error details:
Item: <complex value>
Use "--cli-error-format json" or another error format to see the full details.

<complex value> 就是那個項目,被預設的文字轉譯器藏起來了。加上 --cli-error-format json,整包就會印出來:

{
    "Message": "The conditional request failed",
    "Code": "ConditionalCheckFailedException",
    "Item": {
        "Artist": {"S": "Arturo Sandoval"},
        "Year": {"N": "1994"},
        "Version": {"N": "8"},
        "SongTitle": {"S": "Cubano Chant"},
        "AlbumTitle": {"S": "Danzon"},
        "Genre": {"S": "Latin Jazz"}
    }
}

(屬性對應各自折成一行;其餘皆為原樣輸出。)Version 是 8 而 Genre 已設定,因為第一次執行成功了。那就是從 shell 指令碼閉合的樂觀鎖迴圈:把 stderr 接進 jq -r '.Item.Version.N',把結果餵回 :expectedVersion,然後重試。不需要 get-item,也沒有讀取與重試之間讓第三個寫入者鑽進來的空窗。

重試並不免費。每一次被拒絕的嘗試都會消耗一個寫入單位,所以在緊密迴圈下爭用的鍵會持續計費卻毫無進展。如果你想在設定嘗試次數上限之前先知道一場重試風暴實際上要花多少錢,定價計算機會把寫入速率換算成每月金額。

若想在自己的表格上執行這些防護,又不必跟 shell 的引號與佔位符對應纏鬥,請下載 DynoTable

相關範例

參考資料

最後驗證於 2026-07-28,對照上方連結的 AWS 官方文件。

不必透過主控台就能操作 DynamoDB

一款快速的 DynamoDB 桌面用戶端,可執行 DynamoDB 無法執行的真正 SQL — JOINs、GROUP BY、聚合 — 並支援視覺化編輯與使用你自己的 Bedrock 金鑰的 AI 代理。

30 天免費試用,無需信用卡 — 之後為無時間限制的免費方案。