

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 含版本編號的樂觀鎖定
<a name="BestPractices_OptimisticLocking"></a>

樂觀鎖定是一種策略，可偵測寫入時的衝突，而不是阻止衝突。每個項目都包含一個版本屬性，每次更新都會遞增。更新項目時，您會包含[條件表達](Expressions.ConditionExpressions.md)式，以檢查版本號碼是否符合應用程式上次讀取的值。如果另一個程序同時修改了項目，則條件會失敗，且 DynamoDB 會傳回 `ConditionalCheckFailedException`。

## 何時使用樂觀鎖定
<a name="BestPractices_OptimisticLocking_WhenToUse"></a>

樂觀鎖定非常適合下列情況：
+ 多個使用者或程序可能會更新相同的項目，但衝突不常發生。
+ 重試失敗的寫入對您的應用程式來說並不昂貴。
+ 您想要避免管理分散式鎖定的額外負荷和複雜性。

常見的範例包括電子商務庫存更新、協作編輯平台和金融交易記錄。

## 取捨
<a name="BestPractices_OptimisticLocking_Tradeoffs"></a>

**在高度爭用中重試額外負荷**  
在高並行環境中，衝突發生的可能性提高，可能造成更多重試與更高的寫入成本。

**實作複雜度**  
將版本控制新增至項目和處理條件式檢查，會增加應用程式邏輯的複雜性。適用於 Java v2 的 AWS SDK 增強型用戶端透過 [https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE)註釋提供內建支援，可自動為您管理版本編號。

## 模式設計
<a name="BestPractices_OptimisticLocking_PatternDesign"></a>

在每個項目中包含版本屬性。以下為簡單的結構設計：
+ 分割區索引鍵 – 每個項目的唯一識別符 （例如 `ItemId`)。
+ 屬性：
  + `ItemId` – 項目的唯一識別碼。
  + `Version` – 表示項目版本編號的整數。
  + `QuantityLeft` – 項目的剩餘庫存。

首次建立項目時，`Version` 屬性會設為 1。每次更新時，版本編號會遞增 1。


| ItemID （分割區索引鍵） | 版本 | QuantityLeft | 
| --- | --- | --- | 
| 香蕉 | 1 | 10 | 
| Apples | 1 | 5 | 
| 橘色 | 1 | 7 | 

## 實作
<a name="BestPractices_OptimisticLocking_Implementation"></a>

若要實作樂觀鎖定，請遵循下列步驟：

1. 讀取項目的當前版本。

   ```
   def get_item(item_id):
       response = table.get_item(Key={'ItemID': item_id})
       return response['Item']
   
   item = get_item('Bananas')
   current_version = item['Version']
   ```

1. 使用檢查版本編號的條件表達式更新項目。

   ```
   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
   ```

1. 使用新讀取重試來處理衝突。

   每次重試都需要額外的讀取，因此請限制重試的總數。

   ```
   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 v2 的 AWS SDK 增強型用戶端透過 [https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE)註釋提供內建的樂觀鎖定支援，該註釋會自動為您管理版本編號。

如需條件表達式的詳細資訊，請參閱 [DynamoDB 條件表達式 CLI 範例](Expressions.ConditionExpressions.md)。