Amazon DynamoDB Transactions:工作原理 - Amazon DynamoDB
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

Amazon DynamoDB Transactions:工作原理

借助 Amazon DynamoDB Transactions,您可以将多个操作分组在一起,并将它们作为单个“要么全有要么全无”的 TransactWriteItemsTransactGetItems 操作提交。以下各部分介绍有关在 DynamoDB 中使用事务操作的 API 操作、容量管理、最佳实践和其他详细信息。

TransactWriteItems API

TransactWriteItems 是一个同步和幂等的写入操作,它可将多达 100 个写入操作分组在单个“要么全有要么全无”操作中。这些操作的目标是同一个 Amazon 账户和同一个区域内的一个或多个 DynamoDB 表中有多达 100 个不同的项目。事务中项目的合计大小不能超过 4 MB。这些操作以原子方式完成,以便所有操作都成功或都失败。

注意
  • TransactWriteItems 不同于 BatchWriteItem 操作,因为前者包含的所有操作必须成功完成,否则根本不会进行任何更改。借助 BatchWriteItem 操作,批处理中的某些操作可能获得成功,而其他操作失败。

  • 不能使用索引执行事务。

您不能将同一个事务中的多个操作指向同一个项目。例如,您不能在同一个事务中对相同项目既执行 ConditionCheck 又执行 Update 操作。

您可向事务添加下列类型的操作:

  • Put — 启动 PutItem 操作以创建一个新项目,或者将旧项目替换为新项目(有条件或未指定任何条件)。

  • Update — 启动 UpdateItem 操作以编辑现有项目的属性,或者将新项目添加到表中(如果它不存在)。使用此操作有条件或无条件添加、删除或更新现有项目的属性。

  • Delete — 启动 DeleteItem 操作,以删除表中由其主键标识的单个项目。

  • ConditionCheck — 检查项目是否存在,或者检查项目特定属性的条件。

事务完成后,通过该事务进行的更改将传播到全局二级索引 (GSI)、流和备份。由于传播并不具有即时性,如果在传播过程中某个表从备份还原 (RestoreTableFromBackup) 或将其导出到某个时间点 (ExportTableToPointInTime),则可能会包含在近期事务期间所做的部分而非全部更改。

幂等性

当您执行 TransactWriteItems 调用时,可以选择包含客户端令牌,以确保请求是幂等的。通过使事务成为幂等的,如果相同操作由于连接超时或其他连接问题而多次提交,则可帮助防止出现应用程序错误。

如果原始 TransactWriteItems 调用成功,则使用相同客户端令牌的后续 TransactWriteItems 调用将成功返回,而不做任何更改。如果设置了 ReturnConsumedCapacity 参数,则初始 TransactWriteItems 调用将返回在进行更改时占用的写入容量单位数。使用相同客户端令牌的后续 TransactWriteItems 调用将返回在读取项目时占用的读取容量单位数。

有关幂等性的要点
  • 客户端令牌在使用它的请求完成后的 10 分钟内有效。10 分钟后,任何使用相同客户端令牌的请求都将被视为一个新请求。10 分钟后,您不应为同一请求使用重新相同的客户端令牌。

  • 如果您在 10 分钟的幂等性时段内使用相同的客户端令牌重复请求,但更改了某个其他请求参数,则 DynamoDB 将返回 IdempotentParameterMismatch 异常。

写入错误处理

写入事务在以下情况下不会成功:

  • 其中一个条件表达式中的条件未得到满足时。

  • 因为同一个 TransactWriteItems 操作中的多个操作指向同一个项目而导致发生事务验证错误时。

  • TransactWriteItems 请求与 TransactWriteItems 请求中针对一个或多个项目的正在执行的 TransactWriteItems 操作冲突时。在这种情况下,请求将失败并显示 TransactionCanceledException

  • 当完成事务所需的预置容量不足时。

  • 当项目大小变得过大(大于 400 KB),或者本地二级索引 (LSI) 变得过大,或者由于事务所做更改导致发生类似的验证错误时。

  • 出现用户错误(如数据格式无效)时。

有关如何处理与 TransactWriteItems 操作的冲突的更多信息,请参阅 DynamoDB 中的事务冲突处理

TransactGetItems API

TransactGetItems 是一个同步读取操作,它可将多达 100 个 Get 操作分组在一起。这些操作的目标是同一个 Amazon 账户和区域内的一个或多个 DynamoDB 表中有多达 100 个不同的项目。事务中项目的合计大小不能超过 4 MB。

Get 以原子方式执行,以便所有操作都成功或都失败:

  • Get — 启动 GetItem 操作,以检索具有给定主键的项目的一组属性。如果找不到匹配项目,Get 将不返回任何数据。

读取错误处理

读取事务在以下情况下不会成功:

  • TransactGetItems 请求与 TransactGetItems 请求中针对一个或多个项目的正在执行的 TransactWriteItems 操作冲突时。在这种情况下,请求将失败并显示 TransactionCanceledException

  • 当完成事务所需的预置容量不足时。

  • 出现用户错误(如数据格式无效)时。

有关如何处理与 TransactGetItems 操作的冲突的更多信息,请参阅 DynamoDB 中的事务冲突处理

DynamoDB 事务的隔离级别

事务操作的隔离级别(TransactWriteItemsTransactGetItems)和其他操作如下所示。

可序列化

可序列化隔离可确保多个并发操作的结果相同,就像当前操作在前一个操作完成之前不会开始。

以下操作类型之间具有可序列化隔离:

  • 在任何事务操作与任何标准写入操作(PutItemUpdateItemDeleteItem)之间。

  • 在任何事务操作与任何标准读取操作 (GetItem) 之间。

  • TransactWriteItems 操作与 TransactGetItems 操作之间。

尽管在事务操作与 BatchWriteItem 操作中的每个单独标准写入之间具有可序列化隔离,但作为一个整体,在事务与 BatchWriteItem 操作之间没有可序列化隔离。

类似地,事务操作与 BatchGetItem 操作中的单个 GetItems 之间的隔离级别是可序列化的。但是事务和作为一个单元的 BatchGetItem 操作之间的隔离级别是读取已提交

单个 GetItem 请求可以采用相对于 TransactWriteItems 请求的两种方式序列化,在 TransactWriteItems 请求 之前或之后。多个GetItem请求,针对并发TransactWriteItems请求可以按任何顺序运行,因此结果为读取已提交

例如,如果对项目 A 和项目 B 的 GetItem 请求与修改项目 A 和项目 B 的 TransactWriteItems 请求同时运行,则有四种可能性:

  • 两个 GetItem 请求在 TransactWriteItems 请求之前运行。

  • 两个 GetItem 请求在 TransactWriteItems 请求之后运行。

  • 对项目 A 的 GetItem 请求在 TransactWriteItems 请求之前运行。对于项目 B,GetItemTransactWriteItems 之后运行。

  • 对项目 B 的 GetItem 请求在 TransactWriteItems 请求之前运行。对于项目 A,GetItemTransactWriteItems 之后运行。

如果对于多个 GetItem 请求偏好可序列化隔离级别,则应使用 TransactGetItems

如果对属于传输中的同一事务写入请求的多个项目进行非事务性读取,则有可能能够读取其中一些项目的新状态以及其它项目的过往状态。只有在收到事务写入的成功响应时,您才能读取属于事务写入请求的所有项目的新状态。

读取已提交

读取已提交隔离可确保读取操作始终返回项目的已提交值,对于表现出事务写入未最终成功状态的项目,读取操作永远不会对该项目呈现视图。读取已提交隔离无法防止在读取操作后立即修改项目。

隔离级别在任何事务操作与涉及多个标准读取(BatchGetItemQueryScan)的任何读取操作之间为读取已提交。如果事务写入在 BatchGetItemQueryScan 操作中间更新了某个项目,则该读取操作接下来的部分将返回新的已提交值(对于 ConsistentRead))或可能是之前的已提交值(最终一致性读取)。

操作摘要

简而言之,下表显示事务操作(TransactWriteItemsTransactGetItems)与其他操作之间的隔离级别。

操作 隔离级别

DeleteItem

可序列化

PutItem

可序列化

UpdateItem

可序列化

GetItem

可序列化

BatchGetItem

读取已提交*

BatchWriteItem

不可序列化*

Query

读取已提交

Scan

读取已提交

其他事务操作

可序列化

标记星号 (*) 的级别作为一个整体适用于操作。但是,这些操作内的各个操作具有可序列化隔离级别。

DynamoDB 中的事务冲突处理

事务内的项目上的并发项级请求期间可能会发生事务冲突。在以下情况下可能发生事务冲突:

  • 项目的 PutItemUpdateItemDeleteItem 请求与包含相同项目的正在进行的 TransactWriteItems 请求冲突。

  • TransactWriteItems 请求中的某个项目是另一个正在进行的 TransactWriteItems 请求的一部分。

  • TransactGetItems 请求中的某个项目是正在进行的 TransactWriteItemsBatchWriteItemPutItemUpdateItemDeleteItem 请求的一部分。

注意
  • PutItemUpdateItemDeleteItem 请求被拒绝时,请求会失败,并显示 TransactionConflictException

  • 如果 TransactWriteItemsTransactGetItems 任何项目级别请求被拒绝,则请求将失败并显示 TransactionCanceledException。如果该请求失败,Amazon SDK 不重试请求。

    如果您使用的是 Amazon SDK for Java,则该异常包含 CancellationReasons 列表,该列表根据 TransactItems 请求参数中的项目列表排序。对于其他语言,列表的字符串表示形式包含在异常的错误消息中。

  • 但是,如果正在执行的 TransactWriteItemsTransactGetItems 操作与并发 GetItem 请求冲突,则这两个操作都会成功。

对于每个失败的项目级别请求,TransactionConflict CloudWatch metric 指标会递增。

在 DynamoDB Accelerator(DAX)中使用事务 API

TransactWriteItemsTransactGetItems 在 DynamoDB Accelerator (DAX) 中都受支持,并且其隔离级别与 DynamoDB 中的相同。

TransactWriteItems 通过 DAX 写入。DAX 将一个 TransactWriteItems 调用传递到 DynamoDB,并返回响应。为了在写入后填充缓存,DAX 在后台对 TransactWriteItems 操作中的每个项目调用 TransactGetItems,这将使用额外的读取容量单位。(有关更多信息,请参阅 事务的容量管理。) 利用此功能,您可以保持应用程序逻辑的简单性,并将 DAX 同时用于事务操作和非事务操作。

TransactGetItems 调用将通过 DAX 进行传递,而不会在本地缓存项目。这与 DAX 中的强一致性读取 API 的行为相同。

事务的容量管理

无需其他成本即可为 DynamoDB 表启用事务。您只需为作为事务一部分的读取或写入付费。DynamoDB 在事务中对于每个项目执行两次基础读写:一次是准备事务,一次是提交事务。这两个基础读/写操作显示在 Amazon CloudWatch 指标中。

当您为表预配置容量时,规划事务 API 需要的其他读取和写入。例如,假设您的应用程序每秒执行一个事务,并且每个事务在表中写入三个 500 字节的项目。每个项目需要两个写入容量单位 (WCU):一个用于准备事务,一个用于提交事务。因此,您需要为表预置六个 WCU。

如果您在前面的示例中使用 DynamoDB Accelerator (DAX),则还将为 TransactWriteItems 调用中的每个项目使用两个读取容量单位 (RCU)。因此,您需要为表预配置另外六个 RCU。

同样,如果您的应用程序每秒执行一个读取事务,并且每个事务读取表中三个 500 字节的项目,则您需要为表预置六个读取容量单位 (RCU)。读取每个项目需要两个 RCU:一个用于准备事务,一个用于提交事务。

此外,默认的 SDK 行为是在引发 TransactionInProgressException 异常时重试事务。规划这些重试所占用的其他读取容量单位 (RCU)。这同样适用于您使用 ClientRequestToken 在您自己的代码中重试事务。

事务的最佳实践

使用 DynamoDB 事务时,请考虑以下建议的实践。

  • 对表启用自动扩展功能,或者确保您已预置了足够的吞吐容量,以便为事务中的每个项目执行两个读取或写入操作。

  • 如果您未使用 Amazon 提供的 SDK,则在进行 TransactWriteItems 调用时加入 ClientRequestToken 属性,以确保请求具有幂等性。

  • 如果不必要,请勿将操作分组在一个事务中。例如,如果具有 10 个操作的单个事务可以分解为多个事务而不会妨碍应用程序正确性,则建议您拆分此事务。更简单的事务可提高吞吐量,且更可能成功。

  • 同时更新相同项目的多个事务可能导致冲突而取消事务。我们建议您遵循 DynamoDB 数据建模的最佳实践,以最大限度地减少此类冲突。

  • 如果一组属性经常跨多个项目作为单个事务的一部分进行更新,则考虑将这些属性分组到一个项目,以减小事务的范围。

  • 避免使用事务来成批注入数据。对于成批写入,使用 BatchWriteItem 则更好。

将事务 API 与全局表结合使用

DynamoDB 事务中包含的操作只能保证在最初执行该事务的区域内是事务性的。在事务中应用的更改跨区域复制到全局表副本时,不会保留事务性。

DynamoDB 事务与 AWSLabs 事务客户端库

DynamoDB 事务为 AWSLabs 事务客户端库提供了更经济实惠、强大且高性能的替代方式。我们建议您更新应用程序,以使用原生服务器端事务 API。