

# DynamoDB 事务的悲观锁
<a name="BestPractices_PessimisticLocking"></a>

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

## 使用事务进行锁定的情况
<a name="BestPractices_PessimisticLocking_WhenToUse"></a>

事务非常适合下列情况：
+ 您需要以原子方式更新多个项目，无论是在同一个表还是多个表中。
+ 您的业务逻辑需要全有或全无语义，即要么所有更改都成功，要么不应用任何更改。

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

## 权衡
<a name="BestPractices_PessimisticLocking_Tradeoffs"></a>

**写入成本更高**  
对于不超过 1 KB 的项目，每个项目的事务会消耗 2 WCU（一个用于准备，一个用于提交），而标准写入消耗 1 WCU。

**项目限制**  
一个事务可以包含对一个或多个表的最多 100 个操作。

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

## 实现
<a name="BestPractices_PessimisticLocking_Implementation"></a>

以下示例使用 `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 事务管理复杂工作流](transactions.md)。