View a markdown version of this page

DynamoDB 事务的悲观锁 - Amazon DynamoDB

DynamoDB 事务的悲观锁

DynamoDB 事务为分组操作提供了一种全有或全无的方法。使用 TransactWriteItems 时,DynamoDB 监控事务中的所有项目。在事务处理期间,如果任何项目被其他操作修改,则整个事务将被取消,DynamoDB 将返回 TransactionCanceledException。此行为提供了一种悲观并发控制,因为这样可以防止出现冲突的并发修改,而不是在事后检测。

使用事务进行锁定的情况

事务非常适合下列情况:

  • 您需要以原子方式更新多个项目,无论是在同一个表还是多个表中。

  • 您的业务逻辑需要全有或全无语义,即要么所有更改都成功,要么不应用任何更改。

常见的例子包括账户之间的资金转移、同时更新库存和订单表的下订单行为,以及在游戏中玩家之间交换物品。

权衡

写入成本更高

对于不超过 1 KB 的项目,每个项目的事务会消耗 2 WCU(一个用于准备,一个用于提交),而标准写入消耗 1 WCU。

项目限制

一个事务可以包含对一个或多个表的最多 100 个操作。

冲突敏感性

如果事务中的任何项目被其他操作修改,则整个事务失败。在高争用率的场景中,这可能会导致频繁取消。

实现

以下示例使用 TransactWriteItems 以原子方式在两个项目之间转移库存。如果其他进程在事务处理期间修改了任一项目,则整个操作都将回退。

import boto3 client = boto3.client('dynamodb') def transfer_inventory(source_id, target_id, quantity): try: client.transact_write_items( TransactItems=[ { 'Update': { 'TableName': 'Inventory', 'Key': {'ItemID': {'S': source_id}}, 'UpdateExpression': 'SET QuantityLeft = QuantityLeft - :qty', 'ConditionExpression': 'QuantityLeft >= :qty', 'ExpressionAttributeValues': { ':qty': {'N': str(quantity)} } } }, { 'Update': { 'TableName': 'Inventory', 'Key': {'ItemID': {'S': target_id}}, 'UpdateExpression': 'SET QuantityLeft = QuantityLeft + :qty', 'ExpressionAttributeValues': { ':qty': {'N': str(quantity)} } } } ] ) return True except client.exceptions.TransactionCanceledException as e: print(f"Transaction canceled: {e}") return False

在此示例中,条件表达式检查是否存在足够的库存,但不需要版本属性。在准备阶段和提交阶段之间,如果事务中的任何项目被其他操作修改,DynamoDB 会自动取消事务。这种机制提供了悲观并发控制,事务本身可以防止相互冲突的并发修改。

注意

您可以通过添加版本检查作为附加的条件表达式,将事务与乐观锁结合起来。这可以提供额外的保护措施,但并不是事务检测冲突所必需的。

有关更多信息,请参阅 使用 DynamoDB 事务管理复杂工作流