

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

# Amazon DynamoDB Transactions：運作方式
<a name="transaction-apis"></a>

使用 Amazon DynamoDB Transactions 時，您可以將多項動作歸為一組，然後將這些動作以全有或全無的單一 `TransactWriteItems` 或 `TransactGetItems` 操作提交。下列各節說明 API 操作、容量管理、最佳實務，以及關於在 DynamoDB 中使用交易操作的其他詳細資訊。

**Topics**
+ [TransactWriteItems API](#transaction-apis-txwriteitems)
+ [TransactGetItems API](#transaction-apis-txgetitems)
+ [DynamoDB 交易的隔離層級](#transaction-isolation)
+ [DynamoDB 中的交易衝突處理](#transaction-conflict-handling)
+ [使用 DynamoDB Accelerator (DAX) 中的交易 API](#transaction-apis-dax)
+ [交易的容量管理](#transaction-capacity-handling)
+ [交易的最佳實務](#transaction-best-practices)
+ [將交易 API 與全域資料表搭配使用](#transaction-integration)
+ [DynamoDB 交易與 AWSLabs 用戶端程式庫的比較](#transaction-vs-library)

## TransactWriteItems API
<a name="transaction-apis-txwriteitems"></a>

`TransactWriteItems` 是一項冪等性的同步寫入操作，可將最多 100 個寫入動作分組成為全有或全無的單一操作。這些動作最多可將相同 AWS 帳戶和相同區域中一或多個 DynamoDB 資料表中的 100 個不同項目設為目標。交易中項目的彙總大小不能超過 4 MB。這些動作的完成具有不可分割性，也就是全部成功或全部失敗。

**注意**  
 `TransactWriteItems` 操作與 `BatchWriteItem` 操作的不同之處，在於前者所包含的全部動作，都必須順利地完成，否則就完全不會進行變更。如果是使用 `BatchWriteItem` 操作，即可允許批次中只有某些動作順利完成 (其他的動作失敗)。
 無法使用索引執行交易。

在同一筆交易中，您無法針對同一個項目進行多項操作。例如，您不能對同一筆交易中的同一個項目既執行 `ConditionCheck` 又執行 `Update` 動作。

您可以將下列類型的動作新增到交易中：
+ `Put`：發起 `PutItem` 操作，來建立新的項目，或是用新的項目取代舊的項目 (具有條件或不指定任何條件)。
+ `Update`：發起 `UpdateItem` 操作，來編輯現有項目的屬性，或是在新項目尚未存在時，將新項目加入到資料表。利用此項動作，來新增、刪除或更新現有項目的屬性 (具有條件或無條件)。
+ `Delete`：發起 `DeleteItem` 操作，透過指定資料表中某項目的主索引鍵，來刪除該單一項目。
+ `ConditionCheck`：查看某項目是否存在，或是查看某項目特定屬性的條件。

交易在 DynamoDB 中完成時，其變更會開始傳播至全域次要索引 (GSI)、串流和備份。此傳播會逐步發生：來自相同交易的串流記錄，可能會在不同時間出現，且可能與其他交易的記錄交錯。串流取用者不應假設交易具有原子性或排序保證。

為了確保交易中修改項目的原子快照，請使用 TransactGetItems 操作，以同時讀取所有相關項目。此操作提供一致的資料檢視，確保您所見內容為已完成交易的所有變更，或完全不顯示任何變更。

因為傳播不是立即或瞬時完成，所以如果資料表是從備份還原 ([RestoreTableFromBackup](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_RestoreTableFromBackup.html)) 或匯出到中途傳播的某個時間點 ([ExportTableToPointInTime](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_ExportTableToPointInTime.html))，可能只包含最近交易期間完成的部分變更。

### 冪等性
<a name="transaction-apis-txwriteitems-idempotency"></a>

在進行 `TransactWriteItems` 呼叫時，您可以選擇性地加入用戶端符記，以確保請求具有*冪等性*。如果讓交易具有冪等性，則在因為連線逾時或其他連線問題，而多次提交同一項操作時，有助於防止應用程式發生錯誤。

如果原始的 `TransactWriteItems` 呼叫順利完成，便會成功傳回後續具有相同用戶端字符的 `TransactWriteItems` 呼叫，不建立任何變更。如果設定了 `ReturnConsumedCapacity` 參數，則初始的 `TransactWriteItems` 呼叫會傳回在建立變更時所使用的寫入容量單位數量。後續具有相同用戶端符記的 `TransactWriteItems` 呼叫，會傳回在讀取項目時所用掉的讀取容量單位數量。

**關於冪等性的重點**
+ 用戶端符記在使用該符記的請求完成後 10 分鐘內為有效。10 分鐘過後，使用同一個用戶端符記的任何請求，會被視為新的請求。在經過 10 分鐘之後，您就不應該針對相同的請求，重複使用同一個用戶端符記。
+ 如果您在 10 分鐘的冪等性有效期間內，使用相同的用戶端符記來重複請求，但變更了其他的某些請求參數，則 DynamoDB 會傳回 `IdempotentParameterMismatch` 例外。

### 寫入的錯誤處理
<a name="transaction-apis-txwriteitems-errors"></a>

在下列的情況中，寫入交易不會成功：
+ 當其中一個條件表達式中的條件不符時。
+ 因為在同一個 `TransactWriteItems` 操作中，有超過一個以上的動作鎖定相同的項目，而造成交易驗證錯誤時。
+ 如果 `TransactWriteItems` 請求與 `TransactWriteItems` 請求中一或多個項目持續執行的 `TransactWriteItems` 操作相互衝突時。在這種情況中，請求會失敗，並且丟出 `TransactionCanceledException` 例外。
+ 佈建的容量不足，而使交易無法完成時。
+ 當項目的大小過大 (超過 400 KB)、本機次要索引 (LSI) 變得過大，或是因為交易所進行的變更造成類似的驗證錯誤時。
+ 發生使用者錯誤時，例如無效的資料格式。

 如需如何處理 `TransactWriteItems` 操作衝突的詳細資訊，請參閱 [DynamoDB 中的交易衝突處理](#transaction-conflict-handling)。

## TransactGetItems API
<a name="transaction-apis-txgetitems"></a>

`TransactGetItems` 是一項同步讀取操作，可將最多 100 個 `Get` 動作歸成一組。這些動作最多可將相同 AWS 帳戶和區域中一或多個 DynamoDB 資料表中的 100 個不同項目設為目標。交易中項目的彙總大小不能超過 4 MB。

`Get` 動作的執行具有不可分割性，也就是全部成功或全部失敗：
+ `Get`：發起 `GetItem` 操作，針對具有指定主索引鍵的項目，擷取該項目的一組屬性。如果找不到符合的項目，則 `Get` 不會傳回任何資料。

### 讀取的錯誤處理
<a name="transaction-apis-txgetitems-errors"></a>

在下列的情況中，讀取交易不會成功：
+ 如果 `TransactGetItems` 請求與 `TransactGetItems` 請求中一或多個項目持續執行的 `TransactWriteItems` 操作相互衝突時。在這種情況中，請求會失敗，並且丟出 `TransactionCanceledException` 例外。
+ 佈建的容量不足，而使交易無法完成時。
+ 發生使用者錯誤時，例如無效的資料格式。

 如需如何處理 `TransactGetItems` 操作衝突的詳細資訊，請參閱 [DynamoDB 中的交易衝突處理](#transaction-conflict-handling)。

## DynamoDB 交易的隔離層級
<a name="transaction-isolation"></a>

交易操作 (`TransactWriteItems` 或 `TransactGetItems`) 和其他操作的隔離層級如下。

### 可序列化
<a name="transaction-isolation-serializable"></a>

如果在前一項操作完成之前，沒有任何操作開始執行，則*可序列化*隔離可確保多個同時並行操作的結果會相同。

在下列的操作類型之間，具有可序列化的隔離層級：
+ 在任何交易操作和任何標準寫入操作 (`PutItem`、`UpdateItem` 或 `DeleteItem`) 之間。
+ 在任何交易操作和任何標準讀取操作 (`GetItem`) 之間。
+ 在 `TransactWriteItems` 操作和 `TransactGetItems` 操作之間。

雖然在交易操作之間具有可序列化隔離層級，而且每個個別的標準會在 `BatchWriteItem` 操作中寫入，但是在交易和做為單位的 `BatchWriteItem` 操作之間，不具有可序列化隔離層級。

同樣地，交易操作與 `BatchGetItem` 操作中個別 `GetItems` 之間的隔離層是可以序列化。但是交易與當作一個單位的 `BatchGetItem` 操作之間的隔離層是*專供讀取*。

單一 `GetItem` 請求是兩種可序列化的 `TransactWriteItems` 請求方式之一，可以發生在 `TransactWriteItems` 請求之前或之後。與 `TransactWriteItems` 請求同時發出的多個 `GetItem` 請求，能夠以任何順序執行，因此結果會是*專供讀取*。

例如，如果項目 A 和項目 B 的 `GetItem` 請求與修改項目 A 和項目 B 的 `TransactWriteItems` 請求同時執行，則會有四種可能性：
+ 兩個 `GetItem` 請求皆會在 `TransactWriteItems` 請求之前執行。
+ 兩個 `GetItem` 請求皆會在 `TransactWriteItems` 請求之後執行。
+ 項目 A 的 `GetItem` 請求會在 `TransactWriteItems` 請求之前執行。針對項目 B，`GetItem` 會在 `TransactWriteItems` 之後執行。
+ 項目 B 的 `GetItem` 請求會在 `TransactWriteItems` 請求之前執行。針對項目 A，`GetItem` 會在 `TransactWriteItems` 之後執行。

如果您偏好適用於執行多個 `GetItem` 請求的可序列化隔離等級，應使用 `TransactGetItems`。

如果針對多個項目執行非交易式讀取，而這些項目原本屬於同一筆傳輸中的交易寫入請求，可能會出現部分項目可讀取到新狀態，而其他項目仍顯示舊狀態的情況。唯有在交易成功寫入並收到回應，表示該交易已完成時，您才能讀取原屬於該交易寫入請求之所有項目的新狀態。

交易成功完成並收到回應後，後續的*最終一致*讀取操作在短時間內仍可能傳回舊狀態，這是由於 DynamoDB 的最終一致性模式所致。為了確保在交易後立即讀取最新狀態資料，您應該將 `ConsistentRead` 設定為 true，使用[*高度一致性*](HowItWorks.ReadConsistency.md#HowItWorks.ReadConsistency.Strongly)讀取。

### 專供讀取
<a name="transaction-isolation-read-committed"></a>

*專供讀取*隔離可確保讀取操作對特定項目一律傳回已確認的值 - 如果交易寫入最終未成功，則讀取操作一律不向該項目呈現此寫入狀態的檢視。專供讀取隔離不會防止在讀取操作後立即修改項目。

在任何交易操作和牽涉到多次標準讀取 (`BatchGetItem`、`Query` 或 `Scan`) 的任何讀取操作之間，其隔離層級為專供讀取。如果交易寫入在 `BatchGetItem`、`Query` 或 `Scan` 操作期間更新項目，則讀取操作的後續部分會傳回新確認的值 (含有 `ConsistentRead)` 或可能是先前已確認的值(最終一致讀取)。

### 操作摘要
<a name="transaction-isolation-table"></a>

做為總結，下表顯示了交易操作 (`TransactWriteItems` 或 `TransactGetItems`) 和其他操作之間的隔離層級。


| 作業 | 隔離層級 | 
| --- | --- | 
| `DeleteItem` | *可序列化* | 
| `PutItem` | *可序列化* | 
| `UpdateItem` | *可序列化* | 
| `GetItem` | *可序列化* | 
| `BatchGetItem` | *專供讀取*\* | 
| `BatchWriteItem` | *不可序列化*\* | 
| `Query` | *專供讀取* | 
| `Scan` | *專供讀取* | 
| 其他交易操作 | *可序列化* | 

標有星號的層級 (\*) 適用於單位式操作。不過，這些操作內的個別動作具有*可序列化*的隔離層級。

## DynamoDB 中的交易衝突處理
<a name="transaction-conflict-handling"></a>

在交易內的項目上進行並行項目層級請求期間，可能會發生交易衝突。下列情境可能發生交易衝突：
+ 項目的 `PutItem`、`UpdateItem` 或 `DeleteItem` 請求與包括相同項目的持續 `TransactWriteItems` 請求發生衝突。
+ `TransactWriteItems` 請求內的項目是另一個持續 `TransactWriteItems` 請求的一部分。
+ `TransactGetItems` 請求的項目是持續 `TransactWriteItems`、`BatchWriteItem`、`PutItem`、`UpdateItem` 或 `DeleteItem` 請求的一部分。

**注意**  
當 `PutItem`、`UpdateItem` 或 `DeleteItem` 請求遭到拒絕時，請求失敗並顯示 `TransactionConflictException`。
如果 `TransactWriteItems` 或 `TransactGetItems` 內的項目層級請求遭到拒絕，則請求失敗並顯示 `TransactionCanceledException`。如果請求失敗， AWS SDKs不會重試請求。  
如果您使用的是 適用於 Java 的 AWS SDK，則例外狀況會包含 [CancellationReasons](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CancellationReason.html) 的清單，並根據`TransactItems`請求參數中的項目清單排序。對於其他語言，清單的字串表示法會併入異常的錯誤訊息中。
不過，如果有持續執行的 `TransactWriteItems` 和 `TransactGetItems` 操作，與同時並行的 `GetItem` 請求相互衝突時，這兩項操作都可以順利完成。

對於每個失敗的項目層級請求，[TransactionConflict CloudWatch 指標](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/metrics-dimensions.html)會遞增。

## 使用 DynamoDB Accelerator (DAX) 中的交易 API
<a name="transaction-apis-dax"></a>

DynamoDB Accelerator (DAX) 中支援 `TransactWriteItems` 和 `TransactGetItems`，且隔離層級與在 DynamoDB 中相同。

`TransactWriteItems` 會透過 DAX 寫入。DAX 會將 `TransactWriteItems` 呼叫傳送給 DynamoDB，然後傳回回應。為在寫入後填入快取，對於 `TransactWriteItems` 操作中的每個項目，DAX 會在背景呼叫 `TransactGetItems`，並耗用更多讀取容量單位。(如需詳細資訊，請參閱 [交易的容量管理](#transaction-capacity-handling)。) 此功能可讓您保持簡單的應用程式邏輯，並使用 DAX 執行交易及非交易操作。

`TransactGetItems` 呼叫透過 DAX 傳遞，項目不在本機進行快取。這個行為與 DAX 中的高度一致性讀取 API 相同。

## 交易的容量管理
<a name="transaction-capacity-handling"></a>

在您的 DynamoDB 資料表中啟用交易功能，不需額外付費。您只需針對交易中所進行的讀取和寫入付費即可。DynamoDB 會對交易中的每個項目進行兩項基本的讀取和寫入動作：一項是用來準備交易，一項是用來遞交交易。這兩項基本的讀取/寫入操作，會顯示在您的 Amazon CloudWatch 指標中。

在為資料表佈建容量時，請規劃交易 API 所需的額外讀取與寫入容量。例如，假設您的應用程式每秒執行一項交易，而每項交易會在您的資料表中寫入三個 500 位元組的項目。每個項目需要兩個寫入容量單位 (WCU)：一個單位用來準備交易，另一個單位用來遞交交易。因此，您會需要佈建六個 WCU 給該資料表。

如果您在前一個範例中使用 DynamoDB Accelerator (DAX)，則也會針對 `TransactWriteItems` 呼叫中的每個項目使用兩個讀取容量單位 (RCU)。因此，您會需要佈建六個額外 RCU 給資料表。

同樣地，如果您的應用程式每秒執行一次讀取交易，而每項交易會在您的資料表中讀取三個 500 位元組的項目，則您會需要佈建六個讀取容量單位 (RCU) 給該資料表。讀取每個項目需要兩個 RCU：一個用來準備交易，另一個用來遞交交易。

此外，在出現 `TransactionInProgressException` 例外時，預設的 SDK 動作是重試交易。請規劃這些重試動作會使用的額外讀取容量單位 (RCU)。如果用您自己的程式碼，使用 `ClientRequestToken` 來重試交易時，也請進行同樣的規劃。

## 交易的最佳實務
<a name="transaction-best-practices"></a>

在使用 DynamoDB 交易時，請考慮採用下列建議的做法。
+ 啟用資料表的自動擴展功能，或是確定您已佈建足夠的輸送容量，來針對您交易中的每個項目，進行兩項讀取或寫入操作。
+ 如果您不是使用 AWS 提供的 SDK，請在`TransactWriteItems`呼叫時包含 `ClientRequestToken` 屬性，以確保請求是等冪的。
+ 如非必要，請勿在交易中將操作歸為一組。例如，如果包含 10 項操作的單一交易，可以分成多項交易執行，而不會影響到應用程式的正確度，則我們建議拆分該項交易。簡化的交易可提升輸送量，而且成功的機率更高。
+ 更新同一個項目的多項交易如果同時執行，可能會造成衝突而導致交易取消。我們建議採用下列的 DynamoDB 資料建模最佳實務，來將此等衝突減到最少。
+ 如果在單一交易中，經常更新跨多個項目的一組屬性，請考慮將這些屬性分組成為單一項目，來縮小交易的範圍。
+ 避免使用大量擷取資料的交易。如果是大量寫入作業，較理想的做法是使用 `BatchWriteItem`。

## 將交易 API 與全域資料表搭配使用
<a name="transaction-integration"></a>

交易操作僅在呼叫寫入 API 的 AWS 區域內提供原子性、一致性、隔離性和耐久性 (ACID) 保證。全域資料表不支援跨區域交易。舉例來說，假設您在美國東部 (俄亥俄) 與美國西部 (奧勒岡) 區域中有具有複本的全域資料表，並且在美國東部 (維吉尼亞北部) 區域中執行 `TransactWriteItems` 操作。由於變更受到複寫，所以您可能會在美國西部 (奧勒岡) 區域中看到部分已完成交易。當變更已在來源區域遞交後，這些變更才會複寫至其他區域。

## DynamoDB 交易與 AWSLabs 用戶端程式庫的比較
<a name="transaction-vs-library"></a>

DynamoDB 交易針對 [AWSLabs](https://github.com/awslabs) 交易用戶端程式庫，提供了更具成本效益、更強大和更高效能的替代方法。我們建議您更新應用程式，以使用原生的伺服器端交易 API。