进阶阅读约 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 通过网格的导出操作进行测试装置。

更新于