中階閱讀時間 2 分鐘

DynamoDB ReturnValues:拿回舊項或新項

預設情況下,一次 DynamoDB 寫入除了成功之外什麼都不返回。但你常常需要寫入前後的資料 —— 改動之前的值,或改動之後的新值。最直白的辦法是再做一次 GetItem,這既是一次額外的往返, 是一次競態:別人可以在中間寫入。DynamoDB 用 ReturnValues 引數避開了這兩點,它作為寫入 本身的一部分,原子地把舊項或新項交還給你。

DynamoDB 中的 ReturnValues 有什麼用?

ReturnValues 告訴 DynamoDB 寫入操作在同一次呼叫中把項交還給你,從而跳過第二次 GetItem 及其帶來的競態。PutItemDeleteItem 接受 NONEALL_OLDUpdateItem 接受全部五個選項(NONEALL_OLDUPDATED_OLDALL_NEWUPDATED_NEW),以原子方式返回舊值或新值。

  • ReturnValues 把項作為寫入的一部分返回 —— 沒有第二次讀取,沒有競態。
  • NONE(預設)—— 不返回任何東西。
  • ALL_OLD —— 寫入之前的整個項。
  • UPDATED_OLD —— 僅更新所改動的那些屬性的之前值。
  • ALL_NEW —— 寫入之後的整個項。
  • UPDATED_NEW —— 僅改動過的屬性的之後值。
  • PutItem/DeleteItem 只接受 NONEALL_OLD UpdateItem 接受全部五個。

問題所在:你需要你剛剛覆蓋掉的那個值

假設你營運一個客服臺,一位客服把一張工單的狀態從 open 改成 pending。你的審計日誌需要記錄 改動之前的狀態是什麼。沒有 ReturnValues 的話你會:

  1. GetItem 讀取當前狀態,
  2. UpdateItem 設定新狀態。

在第 1 步和第 2 步之間,另一位客服可能改了狀態 —— 現在你的審計日誌記下的是一個陳舊的「之前」值。 更糟的是,這是一個邏輯操作用了兩次呼叫。ReturnValues 把它收攏成單次原子的 UpdateItem, 返回寫入時刻實際的舊狀態。

五個選項,以及各自何時該用

UpdateItem 支援全套;選擇的是你需要項的哪一部分以及寫入的哪一側

ReturnValues返回何時使用
NONE什麼都不返回你不需要拿回項(預設)
ALL_OLD寫入前的整個項審計 /「我剛替換掉了什麼?」
UPDATED_OLD寫入前改動的屬性你只關心你動過的欄位
ALL_NEW寫入後的整個項你需要把新鮮的完整項返回給呼叫方
UPDATED_NEW寫入後改動的屬性讀回一個你剛自增的計數器/值

UPDATED_NEW 是日常的主力:用一個更新運算式自增一個 計數器,並在同一次呼叫裡讀回新的總數,沒有競態。對於客服工單的審計,ALL_OLD(若你只記錄狀態 欄位則用 UPDATED_OLD)能原子地捕獲改動前的狀態。

注意這種不對稱:PutItemDeleteItem 只支援 NONEALL_OLD —— 刪除沒有「新」值 可返回,而 put 的新值就是你傳送的內容。只有原地修改的 UpdateItem 才提供全部五個。 AWS 文件 給出了確切的對照表。

在 DynoTable 中編寫更新

DynamoDB 運算式構建器視覺化地組裝 UpdateItem 及其 更新運算式 —— 它會生成 SET/ADD 子句以及屬性名和值對映。在應用裡,當一次暫存的寫入被提交後, DynoTable 會顯示生成的項,於是你能直接看到新狀態。

在 DynoTable 中審閱一個項的暫存變更 —— 提交更新前的舊值與新值。
在 DynoTable 中審閱一個項的暫存變更 —— 提交更新前的舊值與新值。

陷阱與後續步驟

  • 別用 GetItem-然後-寫入來讀取一次改動前後的值 —— 那是一次往返加一次競態;用 ReturnValues
  • UPDATED_* 只返回動過的屬性 —— 若你需要整個項,用 ALL_*
  • PutItem/DeleteItem 無法返回新值 —— 只能用 NONE/ALL_OLD
  • ReturnValues 不是條件的替代品 —— 要守衛一次寫入,加一個 條件運算式;要讀回它的效果,用 ReturnValues。 它們可以組合使用。
  • 相關: 更新運算式原子計數器

想做編輯並看到改動前後,而不用指令碼寫兩次呼叫嗎?下載 DynoTable,直接編輯你的項。

原子計數器UPDATED_NEW

庫存系統在每次寫入時都會增加 versionstock 欄位。的模式是一個 UpdateItemADD stock :incReturnValues:更新_新

UpdateItem  PK=SKU#8842
  UpdateExpression: ADD stock :one
  ExpressionAttributeValues: {":one": {"N": "1"}}
  ReturnValues: UPDATED_NEW
Attributes.stock.N == "41"   (was 40)

您僅收到更改後的屬性圖,而不是完整的項目 - 理想情況下商品很大,但呼叫者需要新的櫃檯。對於審計跟蹤,必須捕獲更改前的每個欄位,切換到ALL_OLD。寫入仍按項目大小的 UpdateItem 計費; ReturnValues 確實不新增單獨的閱讀費用 - DynamoDB 已載入該項目以應用更新。

容量備註

返回屬性不會使寫入本身的 WCU 成本增加一倍。你付錢根據每個AWS更新前後的項目大小進行寫入規則,與響應負載中出現的屬性數量無關。如果您想先 GetItem 然後 UpdateItem 記錄舊值,那麼您支付讀加寫的費用。更新中的ReturnValues: ALL_OLD刪除了完整讀取 — 以每秒 500 次更新的速度讀取 2 KB 項目,大約可節省每秒 250 個最終一致的 RCU。

使用條件運算式進行組合

ReturnValuescondition expressions 撰寫同一個電話。示例:僅在低於上限時增加 retryCount,並返回新計數:

ConditionExpression: retryCount < :max
UpdateExpression: ADD retryCount :one
ReturnValues: UPDATED_NEW

如果條件失敗,DynamoDB返回ConditionalCheckFailedException並且無屬性負載 — 與空的成功更新不同 UPDATED_NEW 當一切都沒有改變時。使用 expression builder 生成 UpdateExpression、條件和編組值對映在一起。

決策指南

你需要...設定適用於
沒有回來NONE放置、更新、刪除
覆蓋/刪除之前的完整項目ALL_OLD放置、更新、刪除
之前僅更改了欄位UPDATED_OLD更新
補丁後的完整項目ALL_NEW更新
僅更改欄位,之後UPDATED_NEW更新

刪除並放置

DeleteItemReturnValues: ALL_OLD 是實現“彈出並返回”的方式佇列項的語義 - 刪除的行在 Attributes 中返回。有刪除時沒有ALL_NEW,因為該項目不再存在。當您覆蓋現有項目時,PutItemALL_OLD 返回上一項 key — 對於交換工作流程很有用。當金鑰不存在時,響應省略 Attributes

在DynoTable驗證

在項目編輯器中暫存屬性更改:檢視窗格顯示舊的和新的提交前並排值 — 相同的資訊 UPDATED_OLDUPDATED_NEW 會返回,無需編寫指令碼。提交後,複製該行作為 JSON 透過網格的匯出操作進行測試裝置。

已更新