

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

# DynamoDB 中的資料建模建置區塊
<a name="data-modeling-blocks"></a>

本節介紹建置區塊層，為您提供了可在應用程式中使用的設計模式。

![\[此圖顯示資料間的概念關係、位於其下方的區塊，以及區塊下方的基礎。強調基礎。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/SchemaDesignBlocks.png)


**Topics**
+ [複合排序索引鍵建置區塊](#data-modeling-blocks-composite)
+ [多租用建置區塊](#data-modeling-blocks-multi-tenancy)
+ [稀鬆索引建置區塊](#data-modeling-blocks-sparse-index)
+ [建置區塊生存時間](#data-modeling-blocks-ttl)
+ [封存建置區塊生存時間](#data-modeling-blocks-ttl-archival)
+ [垂直分割建置區塊](#data-modeling-blocks-vertical-partitioning)
+ [寫入碎片建置區塊](#data-modeling-blocks-write-sharding)

## 複合排序索引鍵建置區塊
<a name="data-modeling-blocks-composite"></a>

當人們思考 NoSQL 時，他們也可能認為它是非關聯式的。最終，DynamoDB 結構描述中無法放入關係是不合理的，它們只是看起來與關聯式資料庫及其外部索引鍵不同而已。其中一項可以用來在 DynamoDB 中開發資料邏輯階層的最關鍵模式，就是複合排序索引鍵。最常見的設計模式，是以井字號將階層中的每一層 (父層 > 子層 > 孫層) 隔開。例如 `PARENT#CHILD#GRANDCHILD#ETC`。

![\[此圖顯示資料表中以 UserID 作為主索引鍵，並以其他屬性組合作為排序索引鍵的項目。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/ShoppingCart.png)


雖然 DynamoDB 中的分割區索引鍵一律需要確切的值才能查詢資料，但我們可以將部分條件從左至右套用至排序索引鍵，類似於周遊二元樹。

在上面的範例中，我們有一間需要在使用者工作階段中維護購物車的電子商務商店。每當使用者登入時，他們可能希望看到整個購物車，包括儲存供日後使用的項目。但是，在他們進入結帳程序時，只應載入使用中購物車內的項目以供購買。由於這兩個 `KeyConditions` 都明確要求 CART 排序索引鍵，因此 DynamoDB 會在讀取時直接忽略額外的願望清單資料。雖然儲存的項目和使用中的項目都屬於同一個購物車，但我們需要在應用程式的不同部分中以不同的方式處理它們，因此在排序索引鍵的字首上套用 `KeyCondition`，是僅擷取應用程式每個部分所需資料的最佳方式。

**此建置區塊的主要特徵**
+ 相關項目彼此在本機儲存，以便有效存取資料 
+ 使用 `KeyCondition` 表達式，可以選擇性擷取階層的子集，表示沒有浪費的 RCU 
+ 應用程式的不同部分可以將其項目儲存在特定字首下，防止被覆寫的項目或衝突的寫入

## 多租用建置區塊
<a name="data-modeling-blocks-multi-tenancy"></a>

許多客戶使用 DynamoDB 來託管多租戶應用程式的資料。針對這些案例，我們想要設計結構描述，以便將單一租戶的所有資料保留在其本身的資料表邏輯分割區中。這會利用「項目集合」的概念，該術語是指 DynamoDB 資料表中具有相同分割區索引鍵的所有項目。如需 DynamoDB 如何處理多租戶的詳細資訊，請參閱 [Multitenancy on DynamoDB](https://docs.aws.amazon.com/whitepapers/latest/multi-tenant-saas-storage-strategies/multitenancy-on-dynamodb.html)。

![\[此圖顯示可代表多租戶相片網站的資料表。主索引鍵是由使用者作為分割區索引鍵和不同相片作為排序索引鍵組成。每個項目的屬性都會顯示相片託管所在 URL。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/MultiTenant.png)


在此範例中，我們正在執行一個可能有成千上萬使用者的相片託管網站。每個使用者最初只會將相片上傳到自己的個人資料，但預設情況下，我們不允許使用者查看任何其他使用者的相片。理想情況下，應該為每個使用者呼叫 API 的授權新增額外隔離等級，以確保他們只從自己的分割請求資料，但在結構描述等級中，唯一分割區索引鍵就足夠了。

**此建置區塊的主要特徵**
+ 任何一位使用者或租用戶讀取的資料量只能與其分割區中的項目總量相同
+ 由於帳戶關閉或合規要求而需刪除租戶資料，可以巧妙而廉價的方式完成。只要執行分割區索引鍵等於其租戶 ID 的查詢，然後為傳回的每個主索引鍵執行 `DeleteItem` 操作

**注意**  
專為多租用戶而設計，您可以在單一資料表中使用不同的加密金鑰提供者來安全地隔離資料。適用於 Amazon DynamoDB 的[AWS 資料庫加密 SDK](https://docs.aws.amazon.com/database-encryption-sdk/latest/devguide/what-is-database-encryption-sdk.html) 可讓您在 DynamoDB 工作負載中納入用戶端加密。您可以執行屬性層級加密，讓您在將特定屬性值儲存在 DynamoDB 資料表之前先加密，並搜尋加密的屬性，而無需事先解密整個資料庫。

## 稀鬆索引建置區塊
<a name="data-modeling-blocks-sparse-index"></a>

有時，存取模式需要尋找符合罕見項目的項目，或是接收狀態的項目 (需要提升回應)。我們不會定期查詢這些項目的整個資料集，而是可以利用**全域次要索引 (GSI)** 稀疏的載入資料的事實。這表示只有基礎資料表中具有在索引中定義之屬性項目，才會複寫至索引。

![\[本圖顯示接收大量穩定狀態資料的基礎資料表\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/SparseBaseTable.png)


![\[本圖顯示僅接收已提升項目的全域次要索引\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/SparseGSI.png)


在此範例中，我們可看到 IOT 使用案例，在欄位中的每個裝置都會定期回報狀態。對於大多數報告，我們期望裝置會報告一切正常，但有時可能會出現故障，必須提升給維修技術人員處理。對於具有升級的報告而言，系統會新增該項目的屬性 `EscalatedTo`，但不會顯示該屬性。本範例中的 GSI 由 `EscalatedTo` 分割，由於 GSI 會從基礎資料表中引入金鑰，我們仍然可以看到哪個 DeviceID 報告了故障以及何時報告故障。

雖然在 DynamoDB 中的讀取成本比寫入低，但稀疏索引是非常強大的工具，適用於特定類型項目的執行個體罕見，但讀取以找到它們卻很常見的使用案例。

**此建置區塊的主要特徵**
+ 稀疏 GSI 的寫入和儲存成本僅適用於符合金鑰模式的項目，因此 GSI 的成本可以大幅低於已複製所有項目的其他 GSI 
+ 複合排序索引鍵仍可用來進一步縮小符合所需查詢的項目範圍，例如，排序索引鍵可以使用時間戳記，以便只檢視最後 X 分鐘內報告的錯誤 (`SK > 5 minutes ago, ScanIndexForward: False`)

## 建置區塊生存時間
<a name="data-modeling-blocks-ttl"></a>

大多數資料都有一定的持續時間，可以視為值得保存在主要資料儲存中。為了協助在 DynamoDB 中的資料老化，它具有稱作**存留時間 (TTL)** 的功能。[TTL](TTL.md) 功能可讓您在資料表層級定義特定屬性，這些屬性需要監視具有 epoch 時間戳記 (已為過去的事) 的項目。這使您可以免費從資料表中刪除過期記錄。

**注意**  
如果您使用[全域資料表版本 2019.11.21 (目前版本)](GlobalTables.md) 且您也使用[生存時間](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html)功能，DynamoDB 會複製 TTL 刪除至所有資料表複本。初始 TTL 刪除不會在 TTL 過期發生時消耗區域中的寫入容量。但是，對複本資料表複寫的 TTL 刪除會消耗每個複本區域中的複寫寫入容量，並且將收取適當的費用。

![\[本圖顯示具有存留時間屬性的使用者訊息資料表\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/TTL.png)


在此範例中，我們有一個應用程式專門設計用來讓使用者建立短期訊息。在 DynamoDB 中建立訊息時，應用程式的程式碼會將 TTL 屬性設定為未來七天的日期。在大約七天內，DynamoDB 會看到這些項目的 epoch 時間戳記已經過去，並將其刪除。

由於 TTL 完成的刪除是免費的，因此強烈建議您使用此功能從資料表中移除歷史資料。這將減少每個月的整體儲存體費用，並可能降低使用者讀取成本，因為他們查詢擷取的資料將較少。雖然 TTL 是在資料表層級啟用的，但要為哪些項目或實體建立 TTL 屬性，以及將 epoch 時間戳記設定為未來多久時間，將由您決定。

**此建置區塊的主要特徵**
+ TTL 刪除會在幕後執行，不會影響您的資料表效能 
+ TTL 是一種非同步處理程序，大約每六個小時執行一次，但可能需要超過 48 小時才能刪除過期的記錄 
  + 如果必須在 48 小時內清除過時的資料，請勿在鎖定記錄或狀態管理等使用案例中依賴 TTL 進行刪除 
+ 您可以將 TTL 屬性命名為有效的屬性名稱，但值必須是數字類型

## 封存建置區塊生存時間
<a name="data-modeling-blocks-ttl-archival"></a>

雖然 TTL 是從 DynamoDB 刪除舊資料的有效工具，但許多使用案例都需要將資料封存的保存時間比主要資料儲存更長。在此情況下，我們可以利用 TTL 對記錄的定時刪除，將過期的記錄推送到長期資料儲存。

![\[此圖顯示的資料表會將即時刪除工作的時間傳送至 DynamoDB 串流，然後再傳送長期資料儲存。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/TTLArchive.png)


在 DynamoDB 完成 TTL 刪除時，它仍會作為 `Delete` 事件推送到 DynamoDB 串流中。在 DynamoDB TTL 是執行刪除的執行者時，`principal:dynamodb` 的串流記錄上會有一個屬性。使用 Lambda 訂閱者到 DynamoDB Stream，我們只能為 DynamoDB 主體屬性套用事件篩選條件，並知道符合該篩選條件的任何記錄都會推送到封存存放區，例如 Amazon Glacier。

**此建置區塊的主要特徵**
+  一旦歷史項目不再需要 DynamoDB 的低延遲讀取，將它們遷移到較冷的儲存服務，例如 Amazon Glacier，可以大幅降低儲存成本，同時滿足使用案例的資料合規需求 
+ 如果資料保留在 Amazon S3 中，則可以使用具成本效益的分析工具 (如 Amazon Athena 或 Redshift Spectrum) 來執行資料的歷史分析

## 垂直分割建置區塊
<a name="data-modeling-blocks-vertical-partitioning"></a>

熟悉文件模型資料庫的使用者將熟悉在單一 JSON 文件中儲存所有相關資料的想法。雖然 DynamoDB 支援 JSON 資料類型，但不支援在巢狀 JSON 上執行 `KeyConditions`。由於 `KeyConditions` 會決定要從磁碟讀取多少資料，以及查詢有效消耗了多少 RCU，因此可能會導致大規模的不足。為了更最佳化 DynamoDB 的寫入和讀取，我們建議您將文件的個別實體分割為個別 DynamoDB 項目，也稱為**垂直分割**。

![\[此圖顯示格式化為巢狀 JSON 物件的大型資料結構。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/DocumentBlob.png)


![\[此圖顯示項目集合，其中項目的排序索引鍵有助於保持 DynamoDB 使用率最佳化。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/SingleTableSchema.png)


如上所示，垂直分割是實際操作中單一資料表設計的關鍵範例，但如果需要，也可以跨多個資料表進行實作。由於 DynamoDB 帳單會以 1 KB 的增量進行寫入，因此理想情況下，您應該以產生小於 1 KB 項目的方式對文件進行分割。

**此建置區塊的主要特徵**
+ 資料關係的階層是透過排序索引鍵字首維護的，因此如果需要，可以在用戶端重建單一文件結構 
+ 資料結構的單數元件可以獨立更新，導致小項目更新僅為 1 WCU 
+ 透過使用排序索引鍵 `BeginsWith`，應用程式可在單一查詢中擷取類似資料，彙總讀取成本以降低總成本/延遲
+ 大型文件可輕易地超過 DynamoDB 中的 400 KB 個別項目大小限制，而垂直分割則有助於解決此限制

## 寫入碎片建置區塊
<a name="data-modeling-blocks-write-sharding"></a>

DynamoDB 的極少數硬性限制之一，就是單一實體分割區每秒可維持多少輸送量 (不一定是單一分割區索引鍵) 的限制。這些限制目前是：
+ 1000 個 WCU (或每秒寫入 1000 <= 1KB 的項目) 和 3000 RCU (或每秒讀取 3000 <= 4KB) *強烈一致*或 
+ 每秒 6000 <= 4 KB 讀取*最終一致*

如果對資料表的要求超過上述任一限制，則會將錯誤傳回給 `ThroughputExceededException` 的用戶端 SDK，通常稱為限流。需要超過該限制的讀取操作使用案例，通常是將讀取快取放在 DynamoDB 前面，以達到最佳效果，但是寫入操作需要稱為**寫入碎片**的結構描述層級設計。

![\[此圖顯示 DynamoDB 如何跨多個分割區對分割區索引鍵進行碎片化處理，以防止流量尖峰造成限流。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/WriteShardingProblem.png)


![\[此圖顯示 DynamoDB 如何跨多個分割區對分割區索引鍵進行碎片化處理，以防止流量尖峰造成限流。\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/images/DataModeling/WriteShardingSolution.png)


為了解決此問題，我們將在應用程式的 `UpdateItem` 代碼中的每個競爭者的分割區索引鍵結尾附加一個隨機整數。隨機整數產生器的範圍，將需要具有上限相符或超過指定競爭者每秒的寫入量除以 1000。為了支援每秒 20,000 次投票，它看起來會像 rand (0,19)。現在該資料儲存在單獨的邏輯分割區下，它必須在讀取時重新合併在一起。由於投票總數不需要是即時的，因此排定為每 X 分鐘讀取所有投票分割區的 Lambda 函式可能會偶爾為每位競爭者執行彙總，並將其寫回單一投票總記錄以供即時讀取。

**此建置區塊的主要特徵**
+ 對於無法避免的指定分割區索引鍵具有極高寫入輸送量的使用案例，可以人為將寫入操作分散到多個 DynamoDB 分割區 
+ 具有低基數分割區索引鍵的 GSI 也應該使用此模式，因為 GSI 上的限流會套用背壓，以在基礎資料表上進行寫入操作