

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

# DAX 與 DynamoDB 一致性模式
<a name="DAX.consistency"></a>

Amazon DynamoDB Accelerator (DAX) 是全部寫入快取服務，設計目的是要簡化將快取新增至 DynamoDB 資料表的程序。因為 DAX 與 DynamoDB 分開操作，所以您務必要了解 DAX 和 DynamoDB 的一致性模式，確定應用程式如預期地運作。

在許多使用案例中，應用程式使用 DAX 的方式會影響 DAX 叢集內資料的一致性，以及 DAX 與 DynamoDB 之間資料的一致性。

**Topics**
+ [DAX 叢集節點之間的一致性](#DAX.consistency.nodes)
+ [DAX 項目快取行為](#DAX.consistency.item-cache)
+ [DAX 查詢快取行為](#DAX.consistency.query-cache)
+ [強烈一致和交易讀取](#DAX.consistency.strongly-consistent-reads)
+ [負快取](#DAX.consistency.negative-caching)
+ [寫入策略](#DAX.consistency.strategies-for-writes)

## DAX 叢集節點之間的一致性
<a name="DAX.consistency.nodes"></a>

若要達到應用程式的高可用性，建議您使用至少三個節點來佈建 DAX 叢集。然後將這些節點置放到區域中的多個可用區域內。

DAX 叢集執行時，會複寫叢集中所有節點之間的資料 (假設您已佈建超過一個節點)。請考慮使用 DAX 執行成功 `UpdateItem` 的應用程式。此動作會使用新的值修改主節點中的項目快取。該值接著會複寫到叢集中的所有其他節點。這項複寫最終會一致，而且通常不需要一秒就能完成。

在此情況下，根據每個用戶端所存取的節點，兩個用戶端可能會讀取相同 DAX 叢集中的相同索引鍵，但收到不同的值。在叢集中的所有節點之間完全複寫更新時，這些節點將會一致。(此行為與 DynamoDB 的最終一致性質相似。)

如果您要建置使用 DAX 的應用程式，則應該設計該應用程式，使其容忍最終一致資料。

## DAX 項目快取行為
<a name="DAX.consistency.item-cache"></a>

每個 DAX 叢集有兩個相異快取：*項目*快取與*查詢*快取。如需詳細資訊，請參閱[DAX 的運作方式](DAX.concepts.md)。

本節會介紹讀取和寫入 DAX 項目快取的一致性隱憂。

### 讀取一致性
<a name="DAX.consistency.item-cache.reads"></a>

使用 DynamoDB，`GetItem` 操作預設會執行最終一致讀取。假設您搭配 DynamoDB 用戶端使用 `UpdateItem`。若您接著立即嘗試讀取相同項目，您可能會看到資料顯示的是更新前的內容。這是因為所有 DynamoDB 儲存體位置之間的傳播延遲。通常可在幾秒內達到一致性。因此若您重試讀取，您可能會看到更新後的項目。

當您搭配 DAX 用戶端使用 `GetItem` 時，操作 (在此案例中為最終一致讀取) 會以下列方式繼續。

![\[工作流程圖，顯示加上編號的更新項目步驟。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/dax-item-cache.png)


1. DAX 用戶端發出 `GetItem` 請求。DAX 嘗試從項目快取讀取請求的項目。如果項目位於快取中 (「快取命中」**)，則 DAX 會將其傳回應用程式。

1. 如果項目無法使用 (「快取未中」**)，則 DAX 會對 DynamoDB 執行最終一致的 `GetItem` 操作。

1. DynamoDB 會傳回所請求的項目，而 DAX 會將其存放在項目快取中。

1. DAX 將項目傳回應用程式。

1. (未顯示) 如果 DAX 叢集包含超過一個節點，則會將項目複寫至叢集中的所有其他節點。

根據快取的存留時間 (TTL) 設定及最近最少使用 (LRU) 演算法，項目會保留在 DAX 項目快取中。如需詳細資訊，請參閱[DAX 的運作方式](DAX.concepts.md)。

但是，在此期間，DAX 將不會從 DynamoDB 重新讀取項目。如果有人使用 DynamoDB 用戶端來更新項目，完全略過 DAX，則使用 DynamoDB 用戶端的 `GetItem` 請求所產生的結果會與使用 `GetItem` 用戶端的相同請求不同。在此情況下，除非 DAX 項目的 TTL 過期，否則 DAX 和 DynamoDB 將會針對相同索引鍵保留不一致的值。

如果應用程式修改基礎 DynamoDB 資料表中的資料，略過 DAX，則應用程式需要預期和容忍可能發生的資料不一致。

**注意**  
除了 `GetItem`，DAX 用戶端還支援 `BatchGetItem` 請求。基本上 `BatchGetItem` 是一或多個 `GetItem` 請求的包裝函式，因此 DAX 會將每個請求視為個別的 `GetItem` 操作。

### 寫入一致性
<a name="DAX.consistency.item-cache.writes"></a>

DAX 是全部寫入快取，可簡化讓 DAX 項目快取與基礎 DynamoDB 資料表保持一致的程序。

DAX 用戶端支援與 DynamoDB 相同的寫入 API 操作 (`PutItem`、`UpdateItem`、`DeleteItem`、`BatchWriteItem` 和 `TransactWriteItems`)。當您搭配 DAX 用戶端使用這些操作時，項目會同時在 DAX 和 DynamoDB 中修改。DAX 會更新其項目快取中的項目，無論這些項目的 TTL 值為何。

例如，假設您從 DAX 用戶端發出 `GetItem` 請求，從 `ProductCatalog` 資料表讀取項目。(分割區索引鍵是 `Id`；沒有排序索引鍵。) 您擷取了 `Id` 為 `101` 的項目。該項目的 `QuantityOnHand` 值是 `42`。DAX 會將項目存放在其項目快取中，並具有特定的 TTL。針對此範例，假設 TTL 為 10 分鐘。然後，在 3 分鐘後，另一個應用程式使用 DAX 用戶端來更新相同的項目；因此，該項目的 `QuantityOnHand` 值現在是 `41`。假設未再次更新項目，則在下個 10 分鐘期間，任何後續讀取相同的項目都會傳回已快取的 `QuantityOnHand` 值 (`41`)。

#### DAX 如何處理寫入
<a name="DAX.consistency.item-cache.write-consistency.processing-writes"></a>

DAX 適用於需要高效能讀取的應用程式。身為全部寫入快取，DAX 會同步將您的全部寫入傳遞至 DynamoDB，然後自動地、非同步地將產生的更新複寫到叢集中所有節點的項目快取。您不需要管理快取失效邏輯，因為 DAX 會為您自動進行處理。

DAX 支援下列寫入操作：`PutItem`、`UpdateItem`、`DeleteItem`、`BatchWriteItem` 和 `TransactWriteItems`。

當您將 `PutItem`、`UpdateItem`、`DeleteItem` 或 `BatchWriteItem` 請求傳送至 DAX 時，會執行下列作業：
+ DAX 會將請求傳送至 DynamoDB。
+ DynamoDB 回覆 DAX，確認寫入成功。
+ DAX 將項目寫入至其項目快取。
+ DAX 將成功傳回給申請者。

當您將 `TransactWriteItems` 請求傳送至 DAX 時，會執行下列作業：
+ DAX 會將請求傳送至 DynamoDB。
+ DynamoDB 回覆 DAX，確認交易完成。
+ DAX 將成功傳回給申請者。
+ 在背景中，DAX 會對 `TransactGetItems` 請求中的每個項目進行 `TransactWriteItems` 請求，以將項目存放在項目快取。`TransactGetItems` 會用來確保可[序列化隔離](transaction-apis.md#transaction-isolation-serializable)。

若因為任何原因導致對 DynamoDB 的寫入失敗 (包括限流)，則項目便不會在 DAX 中進行快取。故障異常會傳回給申請者。除非是第一次將資料成功寫入 DAX，否則這會確保不會將資料寫入 DynamoDB 快取。

**注意**  
每個對 DAX 進行的寫入都會改變項目快取的狀態。但是，寫入項目快取不會影響查詢快取。(DAX 項目快取和查詢快取的用途不同，並且會各自獨立操作。)

## DAX 查詢快取行為
<a name="DAX.consistency.query-cache"></a>

DAX 會快取從 `Query` 獲得的結果，並在其查詢快取中進行 `Scan` 請求。但是，這些結果完全不會影響項目快取。在應用程式使用 DAX 發出 `Query` 或 `Scan` 請求後，結果集會存放於查詢快取而非項目快取中。您無法執行 `Scan` 操作來「預熱」項目快取，因為項目快取和查詢快取是不同的實體。

### 查詢-更新-查詢的一致性
<a name="DAX.consistency.query-cache.read-consistency"></a>

項目快取的更新或基礎 DynamoDB 資料表的更新不會讓查詢快取中所存放的結果失效，或對其進行修改。

為了說明，請考慮以下情況。應用程式正在使用 `DocumentRevisions` 資料表，該資料表具有分割區索引鍵 `DocId` 和排序索引鍵 `RevisionNumber`。

1. 用戶端針對 `RevisionNumber` 大於或等於 `5` 的所有項目，發出 `DocId` `101` 的 `Query`。DAX 會將結果集存放在查詢快取中，然後將結果集傳回給使用者。

1. 用戶端針對 `RevisionNumber` 為 `20` 的 `DocId` `101` 發出 `PutItem` 請求。

1. 用戶端發出與步驟 1 所述相同的 `Query` (`DocId` `101` 以及 `RevisionNumber` >= `5`)。

在此案例中，步驟 3 發出的 `Query` 快取結果集會和步驟 1 快取的結果集完全相同。原因是，DAX 不會根據對個別項目的更新，使 `Query` 或 `Scan` 結果集無效。步驟 2 的 `PutItem` 操作只有在 `Query` 的 TTL 過期時，才會反映在 DAX 查詢快取中。

您的應用程式應該考慮查詢快取的 TTL 值，以及應用程式可以容忍查詢快取與項目快取之間不一致結果多長的時間。

## 強烈一致和交易讀取
<a name="DAX.consistency.strongly-consistent-reads"></a>

​若要執行強烈一致 `GetItem`、`BatchGetItem`、`Query` 或 `Scan` 請求，您必須將 `ConsistentRead`​ 參數設為 true。​ DAX 會將高度一致性讀取請求傳遞至 ​DynamoDB。收到來自 DynamoDB 的回應時，DAX 會將結果傳回用戶端，但不會快取結果。DAX 無法提供高度一致性讀取，因為它與 DynamoDB 無法緊密結合。基於此原因，任何從 DAX 進行的後續讀取都必須是最終一致讀取。任何後續的高度一致性讀取都必須傳遞至 DynamoDB。

DAX 處理 `TransactGetItems` 請求的方式與處理高度一致性讀取相同。DAX 會將所有 `TransactGetItems` 請求傳遞至 DynamoDB。收到來自 DynamoDB 的回應時，DAX 會將結果傳回用戶端，但不會快取結果。

## 負快取
<a name="DAX.consistency.negative-caching"></a>

在項目快取和查詢快取中，DAX 都支援負快取項目。「負快取項目」**會在 DAX 於基礎 DynamoDB 資料表中找不到所請求項目時發生。DAX 會快取空的結果，並將該結果傳回使用者，而不是產生錯誤。

例如，假設應用程式將 `GetItem` 請求傳送至 DAX 叢集，而 DAX 項目快取中沒有相符項目。這會讓 DAX 從基礎 DynamoDB 資料表中讀取對應的項目。如果項目不存在於 DynamoDB，則 DAX 會將空項目存放至其項目快取，然後將空項目傳回應用程式。現在假設應用程式傳送同一項目的另一個 `GetItem` 請求。DAX 會找到項目快取中的空項目，並將它立即傳回給應用程式。它完全不會參考 DynamoDB。

負快取項目將會保留在 DAX 項目快取中，直到其項目 TTL 過期、調用 LRU，或是使用 `PutItem`、`UpdateItem` 或 `DeleteItem` 來修改項目。

DAX 查詢快取會以類似的方式處理負快取結果。如果應用程式執行 `Query` 或 `Scan`，而且 DAX 查詢快取未包含已快取的結果，則 DAX 會將請求傳送給 DynamoDB。如果結果集中沒有相符項目，則 DAX 會將空結果集存放至查詢快取，並將空結果集傳回應用程式。除非該結果集的 TTL 過期，否則後續的 `Query` 或 `Scan` 請求將會產生相同的 (空) 結果集。

## 寫入策略
<a name="DAX.consistency.strategies-for-writes"></a>

DAX 的全部寫入行為適用於許多應用程式模式。但是，有些應用程式模式不適用全部寫入模式。

針對對延遲相當敏感的應用程式，全部寫入 DAX 會造成額外的網路跳轉。因此寫入 DAX 會比直接寫入 DynamoDB 來得稍慢。若您的應用程式對寫入延遲相當敏感，您可以透過改為直接寫入 DynamoDB 來降低延遲。如需詳細資訊，請參閱[繞過式寫入](#DAX.consistency.strategies-for-writes.write-around)。

針對寫入密集應用程式 (例如，執行大量資料載入的應用程式)，您可能會不想要透過 DAX 寫入所有資料，因為應用程式只會讀取該資料的極小部分。當您透過 DAX 寫入大量資料時，必須調用其 LRU 演算法來清出快取中的空間，以讀取新項目。這會降低 DAX 作為讀取快取的有效性。

當您將項目寫入 DAX 時，項目快取狀態會變更，以容納新的項目。(例如，DAX 可能需要移出項目快取中較舊的資料，以清出空間來放置新項目。) 新項目將會根據快取的 LRU 演算法以及 TTL 設定保留在項目快取中。只要項目持續存在於項目快取中，DAX 就不會從 DynamoDB 重新讀取項目。

### 全部寫入
<a name="DAX.consistency.strategies-for-writes.write-through"></a>

DAX 項目快取會實作全部寫入政策。如需詳細資訊，請參閱[DAX 如何處理寫入](#DAX.consistency.item-cache.write-consistency.processing-writes)。

當您寫入項目時，DAX 會確保已快取的項目與存在於 DynamoDB 中的項目同步。針對在寫入項目之後需要立即重新讀取項目的應用程式，這十分有用。不過，如果其他應用程式直接寫入 DynamoDB 資料表，則 DAX 項目快取中的項目將不再與 DynamoDB 同步。

為了示範，請考慮正在使用 `ProductCatalog` 資料表的兩位使用者 (Alice 和 Bob)。Alice 使用 DAX 存取資料表，但 Bob 略過 DAX，並在 DynamoDB 中直接存取資料表。

![\[工作流程圖，顯示使用者 Alice 和 Bob 透過 DAX 和 DynamoDB 存取資料表的編號步驟。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/dax-consistency-alice-bob.png)


1. Alice 更新中的 `ProductCatalog` 資料表中的項目。DAX 將請求轉送至 DynamoDB，且更新成功。DAX 接著將項目寫入至其項目快取，並將成功回應傳回給 Alice。從這個時間點開始，除非最後從快取中移出項目，否則任何從 DAX 讀取項目的使用者都會看到具有 Alice 更新的項目。

1. 稍後，Bob 更新了 Alice 寫入的相同 `ProductCatalog` 項目。不過，Bob 直接在 DynamoDB 中更新項目。DAX 不會回應透過 DynamoDB 進行的更新，來重新整理其項目快取。因此，DAX 使用者將不會看到 Bob 的更新。

1. Alice 再次從 DAX 中讀取項目。項目位於項目快取中，因此 DAX 會將它傳回 Alice，而不會存取 DynamoDB 資料表。

在此情況下，Alice 和 Bob 會看到相同 `ProductCatalog` 項目的不同呈現。除非 DAX 從項目快取中移出項目，或另一位使用者使用 DAX 再次更新相同的項目，否則就會是這種情況。

### 繞過式寫入
<a name="DAX.consistency.strategies-for-writes.write-around"></a>

如果您的應用程式需要寫入大量資料 (例如大量資料載入)，則略過 DAX，將資料直接寫入至 DynamoDB 可能會相當合理。這種「繞過式寫入」**策略可減少延遲。但是，項目快取將不會和 DynamoDB 中的資料保持同步。

如果您決定使用繞過式寫入策略，請記住，只要應用程式使用 DAX 用戶端讀取資料，DAX 就會填入其項目快取。這在某些情況下十分有益，因為它確定只會快取最常讀取的資料 (與最常寫入的資料相反)。

例如，請考慮想要使用 DAX 處理不同資料表 (`GameScores` 資料表) 的使用者 (Charlie)。`GameScores` 的分割區索引鍵是 `UserId`；因此，所有 Charlie 的分數都會有相同的 `UserId`。

![\[工作流程圖，顯示 Charlie 如何透過 DAX 使用 DynamoDB 的編號步驟。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/dax-consistency-charlie.png)


1. Charlie 想要擷取其所有的分數，因此他傳送 `Query` 至 DAX。假設先前從未發出過此查詢，DAX 會將查詢轉送至 DynamoDB 進行處理。它會將結果存放在 DAX 查詢快取中，然後將結果傳回給 Charlie。除非移出結果集，否則結果集仍然會位於查詢快取中。

1. 現在假設 Charlie 玩 Meteor Blasters 遊戲，並得到高分。Charlie 將 `UpdateItem` 請求傳送至 DynamoDB，修改 `GameScores` 資料表中的項目。

1. 最後，Charlie 決定重新執行他稍早發出的 `Query` 來從 `GameScores` 擷取他的所有資料。在結果中，Charlie 看不到他在 Meteor Blasters 中的高分。原因是查詢結果來自查詢快取，而不是項目快取。兩個快取彼此獨立，因此其中一個快取中的變更不會影響另一個快取。

DAX 不會使用 DynamoDB 中的最新資料來重新整理查詢快取中的結果集。查詢快取中的每個結果集，其狀態都會是執行 `Query` 或 `Scan` 操作時的狀態。因此，Charlie 的 `Query` 結果不會反映其 `PutItem` 操作。除非 DAX 從查詢快取中移出結果集，否則就會是這種情況。