乐观锁(使用版本号)
乐观锁是一种在写入时检测冲突而不是防止冲突的策略。每个项目都包含一个版本属性,其值会随着每次更新而增加。更新项目时,您需要包含一个条件表达式,用于检查版本号是否与应用程序上次读取的值匹配。如果在这个期间其他进程修改了该项目,则条件失败并且 DynamoDB 返回 ConditionalCheckFailedException。
使用乐观锁的情况
乐观锁非常适合用于以下情况:
多个用户或进程可能会更新同一个项目,但冲突并不频繁。
对于应用程序来说,重试失败写入的成本较低。
您需要避免管理分布式锁的开销和复杂性。
常见的示例包括电子商务库存更新、协作编辑平台和金融交易记录。
权衡
- 高争用率下的重试开销
在高并发环境中,发生冲突的可能性会增加,进而可能导致更高的重试次数和写入成本。
- 实施复杂性
向项目添加版本控制和处理有条件检查,会增加应用程序逻辑的复杂性。适用于 Java 的 AWS SDK v2 增强版客户端通过
@DynamoDbVersionAttribute注解提供内置支持,这可以自动为您管理版本号。
模式设计
每个项目中包含一个版本属性。以下提供了一个简单的架构设计:
分区键:每个项目的唯一标识符(例如
ItemId)。属性:
ItemId– 项目的唯一标识符。Version– 表示项目版本号的整数。QuantityLeft– 项目的剩余库存。
首次创建项目时,Version 属性设置为 1。每次进行更新时,版本号增加 1。
| ItemID(分区键) | 版本 | QuantityLeft |
|---|---|---|
| Bananas | 1 | 10 |
| Apples | 1 | 5 |
| Oranges | 1 | 7 |
实现
要实施乐观锁,请按照以下步骤操作:
-
读取项目的当前版本。
def get_item(item_id): response = table.get_item(Key={'ItemID': item_id}) return response['Item'] item = get_item('Bananas') current_version = item['Version'] -
使用检查版本号的条件表达式更新项目。
def update_item(item_id, qty_bought, current_version): try: response = table.update_item( Key={'ItemID': item_id}, UpdateExpression="SET QuantityLeft = QuantityLeft - :qty, Version = :new_v", ConditionExpression="Version = :expected_v", ExpressionAttributeValues={ ':qty': qty_bought, ':new_v': current_version + 1, ':expected_v': current_version }, ReturnValues="UPDATED_NEW" ) return response except ClientError as e: if e.response['Error']['Code'] == 'ConditionalCheckFailedException': print("Version conflict: another process updated this item.") raise -
使用新的读取进行重试来处理冲突。
每次重试都需要进行额外的读取操作,因此请限制重试的总次数。
def update_with_retry(item_id, qty_bought, max_retries=3): for attempt in range(max_retries): item = get_item(item_id) try: return update_item(item_id, qty_bought, item['Version']) except ClientError as e: if e.response['Error']['Code'] != 'ConditionalCheckFailedException': raise print(f"Retry {attempt + 1}/{max_retries}") raise Exception("Update failed after maximum retries.")
对于 Java 应用程序,适用于 Java 的 AWS SDK v2 增强版客户端通过 @DynamoDbVersionAttribute 注解提供内置乐观锁支持,这可以自动为您管理版本号。
有关条件表达式的更多信息,请参阅 DynamoDB 条件表达式 CLI 示例。