DynamoDB ReturnValues:从一次写入拿回旧项或新项
默认情况下,一次 DynamoDB 写入除了成功之外什么都不返回。但你常常需要写入前后的数据 ——
改动之前的值,或改动之后的新值。最直白的办法是再做一次 GetItem,这既是一次额外的往返,也
是一次竞态:别人可以在中间写入。DynamoDB 用 ReturnValues 参数避开了这两点,它作为写入
本身的一部分,原子地把旧项或新项交还给你。
DynamoDB 中的 ReturnValues 有什么用?
ReturnValues 告诉 DynamoDB 写入操作在同一次调用中把项交还给你,从而跳过第二次 GetItem 及其带来的竞态。PutItem 和 DeleteItem 接受 NONE 或 ALL_OLD;UpdateItem 接受全部五个选项(NONE、ALL_OLD、UPDATED_OLD、ALL_NEW、UPDATED_NEW),以原子方式返回旧值或新值。
ReturnValues把项作为写入的一部分返回 —— 没有第二次读取,没有竞态。NONE(默认)—— 不返回任何东西。ALL_OLD—— 写入之前的整个项。UPDATED_OLD—— 仅更新所改动的那些属性的之前值。ALL_NEW—— 写入之后的整个项。UPDATED_NEW—— 仅改动过的属性的之后值。PutItem/DeleteItem只接受NONE或ALL_OLD;UpdateItem接受全部五个。
问题所在:你需要你刚刚覆盖掉的那个值
假设你运营一个客服台,一位客服把一张工单的状态从 open 改成 pending。你的审计日志需要记录
改动之前的状态是什么。没有 ReturnValues 的话你会:
GetItem读取当前状态,UpdateItem设置新状态。
在第 1 步和第 2 步之间,另一位客服可能改了状态 —— 现在你的审计日志记下的是一个陈旧的「之前」值。
更糟的是,这是一个逻辑操作用了两次调用。ReturnValues 把它收拢成单次原子的 UpdateItem,
返回写入时刻实际的旧状态。
五个选项,以及各自何时该用
UpdateItem 支持全套;选择的是你需要项的哪一部分以及写入的哪一侧:
ReturnValues | 返回 | 何时使用 |
|---|---|---|
NONE | 什么都不返回 | 你不需要拿回项(默认) |
ALL_OLD | 写入前的整个项 | 审计 /「我刚替换掉了什么?」 |
UPDATED_OLD | 写入前改动的属性 | 你只关心你动过的字段 |
ALL_NEW | 写入后的整个项 | 你需要把新鲜的完整项返回给调用方 |
UPDATED_NEW | 写入后改动的属性 | 读回一个你刚自增的计数器/值 |
UPDATED_NEW 是日常的主力:用一个更新表达式自增一个
计数器,并在同一次调用里读回新的总数,没有竞态。对于客服工单的审计,ALL_OLD(若你只记录状态
字段则用 UPDATED_OLD)能原子地捕获改动前的状态。
注意这种不对称:PutItem 和 DeleteItem 只支持 NONE 和 ALL_OLD —— 删除没有「新」值
可返回,而 put 的新值就是你发送的内容。只有原地修改的 UpdateItem 才提供全部五个。
AWS 文档
给出了确切的对照表。
在 DynoTable 中编写更新
用 DynamoDB 表达式构建器可视化地组装 UpdateItem 及其
更新表达式 —— 它会生成 SET/ADD 子句以及属性名和值映射。在应用里,当一次暂存的写入被提交后,
DynoTable 会显示生成的项,于是你能直接看到新状态。

陷阱与下一步
- 别用
GetItem-然后-写入来读取一次改动前后的值 —— 那是一次往返加一次竞态;用ReturnValues。 UPDATED_*只返回动过的属性 —— 若你需要整个项,用ALL_*。PutItem/DeleteItem无法返回新值 —— 只能用NONE/ALL_OLD。ReturnValues不是条件的替代品 —— 要守卫一次写入,加一个 条件表达式;要读回它的效果,用ReturnValues。 它们可以组合使用。- 相关: 更新表达式、 原子计数器。
想做编辑并看到改动前后,而不用脚本写两次调用吗?下载 DynoTable,直接编辑你的项。


