DynamoDB 中的本機次要索引 - Amazon DynamoDB

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

DynamoDB 中的本機次要索引

某些應用程式只需要使用基礎資料表的主索引鍵查詢資料。但是,在某些情況下,替代排序索引鍵會有所幫助。為了給應用程式提供排序索引鍵選擇,您可以在 Amazon DynamoDB 資料表上建立一或多個本機次要索引,並針對這些索引發出 QueryScan 請求。

案例:使用本機次要索引

例如,請考慮Thread資料表。此資料表適用於應用程式,例如 AWS 開發論壇。下圖顯示這些項目在資料表中的組織方式。(並未顯示所有屬性。)

主題資料表包含論壇名稱、話題、上次貼文時間和回覆次數的清單。

DynamoDB 會連續儲存具有相同分割區索引鍵值的所有項目。在本例中,給定一個特定的 ForumNameQuery 操作就可以立即找到該論壇的所有主題。在具有相同分割區索引鍵值的項目群組中,項目會依排序索引鍵值排序。如果查詢中還提供了排序索引鍵 (Subject),DynamoDB 可以縮小傳回的結果範圍,例如傳回「S3」論壇中以字母「a」開頭的 Subject 的所有主題。

某些請求可能需要更複雜的資料存取模式。例如:

  • 哪些論壇主題獲得最多的觀看次數和回覆?

  • 特定論壇中哪個主題的訊息數量最多?

  • 在特定時段內,有多少個主題發佈在特定論壇?

若要回答這些問題,Query 動作並不足夠。您還必須 Scan 整個資料表。對於包含數百萬個項目的資料表,這會消耗大量佈建的讀取輸送量,而且需要很長的時間才能完成。

不過,您可以在非索引鍵屬性上指定一或多個本機次要索引,例如 RepliesLastPostDateTime

本機次要索引會針對指定分割區索引鍵值維護替代排序索引鍵。本機次要索引也包含基礎資料表中部分或全部屬性的複本。您可以指定在建立資料表時要投影到本機次要索引中的屬性。本機次要索引中的資料由與基礎資料表相同的分割區索引鍵組織,但具有不同的排序索引鍵。這可讓您在此不同維度之間有效地存取資料項目。為獲得更大的查詢或掃描靈活性,您最多可以為每個資料表建立五個本機次要索引。

假設應用程式需要尋找在最近三個月內張貼於特定論壇中的所有主題。如果沒有本機次要索引,應用程式必須 Scan 整個 Thread 資料表,並捨棄任何不在指定時間範圍內的貼文。使用本機次要索引,Query 操作可以使用 LastPostDateTime 作為排序索引鍵,並快速找到資料。

下圖顯示名為 LastPostIndex 的本機次要索引。請注意,分割區索引鍵與 Thread 資料表的分割區索引鍵相同,但排序索引鍵是 LastPostDateTime

LastPostIndex 包含論壇名稱、主題和上次發佈時間清單的資料表。

每個本機次要索引均須符合下列條件:

  • 分割區索引鍵與其基礎資料表的分割區索引鍵相同。

  • 排序索引鍵只包含一個純量屬性。

  • 基礎資料表的排序索引鍵會投影到索引中,在索引中充當非索引鍵屬性。

在本例中,分割區索引鍵是 ForumName,而本機次要索引的排序索引鍵是 LastPostDateTime。此外,基礎資料表中的排序索引鍵值 (在本例中為 Subject) 會投影到索引中,但不是索引鍵的一部分。如果應用程式需要以 ForumNameLastPostDateTime 為基礎的清單,則可針對 LastPostIndex 發出 Query 請求。查詢結果會依 LastPostDateTime 排序,並且可以依升序或降序排列傳回。查詢也可以套用索引鍵條件,例如僅傳回在特定的時間範圍內具有 LastPostDateTime 的項目。

每個本機次要索引會自動包含來自其基礎資料表的分割區索引鍵和排序索引鍵;您可以選擇將非索引鍵屬性投影到索引中。當您查詢索引時,DynamoDB 可有效率地擷取這些投影屬性。在查詢本機次要索引時,查詢還可以檢索投影到索引中的屬性。DynamoDB 會自動從基礎資料表擷取這些屬性,但延遲較大,且佈建的輸送量成本也較高。

對於任何本機次要索引,每個不同的分割區索引鍵值最多可存放 10 GB 的資料。此圖包含基礎資料表中的所有項目,以及索引中具有相同分割區索引鍵值的所有項目。如需詳細資訊,請參閱 本機次要索引中的項目集合

屬性投影

應用程式可以借助 LastPostIndexForumNameLastPostDateTime 用作查詢條件。不過,若要擷取任何其他屬性,DynamoDB 必須對 Thread 資料表執行額外的讀取操作。這些額外讀取稱為擷取,可以增加查詢所需的佈建輸送量總量。

假設您想要填入一個網頁,其中包含「S3」中所有主題的清單,以及每個主題的回覆次數 (依上次回覆日期/時間,從最近回覆開始排序)。若要填入此清單,您需要下列屬性:

  • Subject

  • Replies

  • LastPostDateTime

查詢這些資料並避免擷取操作的最有效方法是將 Replies 屬性從資料表投影至本機次要索引,如此圖所示。

LastPostIndex 資料表包含論壇名稱、上次發佈時間、主題和回應的清單。

投影是指從資料表複製到次要索引的屬性集合。資料表的分割區索引鍵和排序索引鍵一律會投影到索引中;您可以投影其他屬性來支援應用程式的查詢需求。查詢索引時,Amazon DynamoDB 可以存取投影中的任何屬性,就好像這些屬性在它們自己的資料表中一樣。

在建立次要索引時,您需要指定要投影到索引中的屬性。DynamoDB 為此提供三種不同的選項:

  • KEYS_ONLY – 索引中的每個項目僅包含資料表分割區索引鍵和排序索引鍵值,以及索引鍵值。KEYS_ONLY 選項會產生最小的可行次要索引。

  • INCLUDE – 除了 中所述的屬性之外KEYS_ONLY,次要索引還將包含您指定的其他非金鑰屬性。

  • ALL – 次要索引包含來源資料表中的所有屬性。因為索引中的所有資料表資料都會重複,所以 ALL 投影會產生最大的可行次要索引。

在上圖中,非索引鍵屬性 Replies 投影到 LastPostIndex。應用程式可以查詢 LastPostIndex,而不是完整的 Thread 資料表,以便使用 SubjectRepliesLastPostDateTime 來填充網頁。如果請求任何其他非索引鍵屬性,DynamoDB 需要從 Thread 資料表擷取這些屬性。

從應用程式的角度來看,從基礎資料表擷取其他屬性是自動且透明的程序,因此不需要重寫任何應用程式邏輯。不過,此類擷取會大幅降低使用本機次要索引的效能優勢。

在選擇要投影到本機次要索引的屬性時,您必須權衡佈建輸送量成本和儲存成本:

  • 若您只需要存取少量的屬性,並且希望盡可能的降低延遲,建議您考慮只將那些屬性投影到本機次要索引。索引愈小,存放成本就愈低,您的寫入成本也愈低。如果您偶爾需要擷取屬性,佈建輸送量的成本可能會超過儲存這些屬性的長期成本。

  • 如果應用程式會頻繁存取某些非索引鍵屬性,您應該考慮將這些屬性投影到本機次要索引。本機次要索引的額外儲存成本會抵銷頻繁執行資料表掃描的成本。

  • 如果需要頻繁存取大多數的非索引鍵屬性,您可以將這些屬性 (或整個基礎資料表) 投影到本機次要索引。這可為您提供最大的靈活性和最低的佈建輸送量耗用量,因為不需要擷取。但是,如果投影所有屬性,您的儲存成本將會增加,甚至翻倍。

  • 如果您的應用程式需要不常查詢資料表,但必須針對資料表中的資料執行許多寫入或更新,請考慮投影 KEYS_ONLY。本機次要索引的大小將會最小,但仍能在需要的時候進行查詢活動。

建立本機次要索引

若要在資料表上建立具有一或多個本機次要索引,請使用 CreateTable 操作的 LocalSecondaryIndexes 參數。建立資料表時,會建立資料表上的本機次要索引。當您使用刪除資料表時,也會刪除該資料表上的任何本機次要索引。

您必須指定一個非索引鍵屬性作為本機次要索引的排序索引鍵。您選擇的屬性必須是純量 StringNumberBinary。不允許其他純量類型、文件類型和集合類型。如需資料類型的完整清單,請參閱 資料類型

重要

對於具有本機次要索引的資料表,每個分割區索引鍵值都有 10 GB 的大小限制。具有本機次要索引的資料表可以存放任何數量的項目,只要任何一個分割區索引鍵值的總大小不超過 10 GB 即可。如需詳細資訊,請參閱 項目集合大小限制

您可以將任何資料類型的屬性投影到本機次要索引。這包括純量、文件和集合。如需資料類型的完整清單,請參閱 資料類型

從本機次要索引讀取資料

您可以使用 QueryScan 操作從本機次要索引擷取項目。GetItemBatchGetItem 操作不能用於本機次要索引。

查詢本機次要索引

在 DynamoDB 資料表中,每個項目的組合分割區索引鍵值和排序索引鍵值必須是唯一的值。不過,在本機次要索引中,排序索引鍵值對於指定的分割區索引鍵值不一定是唯一的值。如果本機次要索引中有多個項目具有相同的排序索引鍵值,Query 操作會傳回具有相同分割區索引鍵值的所有項目。在回應中,符合的項目不會以任何特定的順序傳回。

您可以使用最終一致或強烈一致讀取來查詢本機次要索引。若要指定您想要的一致性類型,請使用 Query 操作的 ConsistentRead 參數。來自本機次要索引的強烈一致讀取一律會傳回最新的更新值。如果查詢需要從基礎資料表中取得其他屬性,這些屬性將與索引保持一致。

範例

請考慮從 Query (請求來自特定論壇主題的資料) 傳回的下列資料。

{ "TableName": "Thread", "IndexName": "LastPostIndex", "ConsistentRead": false, "ProjectionExpression": "Subject, LastPostDateTime, Replies, Tags", "KeyConditionExpression": "ForumName = :v_forum and LastPostDateTime between :v_start and :v_end", "ExpressionAttributeValues": { ":v_start": {"S": "2015-08-31T00:00:00.000Z"}, ":v_end": {"S": "2015-11-31T00:00:00.000Z"}, ":v_forum": {"S": "EC2"} } }

在此查詢中:

  • DynamoDB LastPostIndex使用ForumName分割區索引鍵來尋找 "EC2" 的索引項目。所有具有此索引鍵的索引項目都會相鄰存放,以利快速擷取。

  • 在此論壇中,DynamoDB 使用索引來尋找符合指定 LastPostDateTime 條件的索引鍵。

  • 由於 Replies 屬性投影到索引中時,DynamoDB 可以檢索此屬性,而不會耗用任何額外的佈建輸送量。

  • Tags 屬性不會投影到索引中,因此 DynamoDB 必須存取 Thread 資料表並擷取此屬性。

  • 結果會傳回,並依 LastPostDateTime 排序。索引項目依分割區索引鍵值排序,再依排序索引鍵值排序,然後由 Query 依儲存的順序將其傳回。(您可以使用 ScanIndexForward 參數,以遞減順序傳回結果。)

由於 Tags 屬性未投影到本機次要索引中,DynamoDB 必須消耗額外的讀取容量單位,才能從基礎資料表擷取此屬性。如果需要經常執行此查詢,您應將 Tags 投影至 LastPostIndex,避免從基礎資料表擷取。不過,如果您僅偶爾需要存取 Tags,則將 Tags 投影至索引中的額外儲存成本便可能不值得。

掃描本機次要索引

您可以使用 Scan 從本機次要索引檢索所有資料。您必須在請求中提供基礎資料表名稱及索引名稱。使用 Scan,DynamoDB 會讀取索引中的所有資料,並將其傳回應用程式。您也可以請求只傳回一部分的資料,並捨棄其他剩餘的資料。若要這麼做,請使用 ScanFilterExpression 參數API。如需詳細資訊,請參閱掃描的篩選條件表達式

項目寫入和本機次要索引

DynamoDB 會自動讓所有本機次要索引與各自的基本資料表同步。應用程式永遠不會直接寫入索引。然而,了解 DynamoDB 維持這些索引之方式的含義很重要。

在建立本機次要索引時,您可以指定一個屬性作為索引的排序索引鍵。您也可以為該屬性指定資料類型。這表示每次您將項目寫入基礎資料表時,如果項目定義了索引鍵屬性,則其類型必須與索引鍵結構描述的資料類型相符。在 LastPostIndex 案例中,索引的 LastPostDateTime 排序索引鍵是定義為 String 資料類型。如果您嘗試在 Thread 資料表中新增項目,並為 LastPostDateTime (如 Number) 指定不同的資料類型,DynamoDB 會因資料類型不符而傳回 ValidationException

基本資料表中的項目與本機次要索引中的項目之間沒有關係的要求 one-to-one。事實上,這種行為對許多應用程式來說可能是有利的。

相較於索引較少的資料表,本機次要索引較多的資料表會有較高的寫入活動成本。如需詳細資訊,請參閱 本機次要索引的佈建輸送量考量

重要

對於具有本機次要索引的資料表,每個分割區索引鍵值都有 10 GB 的大小限制。具有本機次要索引的資料表可以存放任何數量的項目,只要任何一個分割區索引鍵值的總大小不超過 10 GB 即可。如需詳細資訊,請參閱 項目集合大小限制

本機次要索引的佈建輸送量考量

在 DynamoDB 中建立資料表時,您可以為資料表的預期工作負載佈建讀取和寫入容量單位。該工作負載包括在資料表本機次要索引上的讀取和寫入活動。

若要檢視佈建的輸送容量的目前費率,請參閱 Amazon DynamoDB 定價

讀取容量單位

在查詢本機次要索引時,消耗的讀取容量單位數目取決於資料的存取方式。

與資料表查詢一樣,索引查詢可以使用最終一致性或強烈一致讀取,取決於 ConsistentRead 的值。一個強烈一致讀取會消耗一個讀取容量單位;最終一致讀取則只會消耗一半的讀取容量單位。因此,透過選擇最終一致讀取,您可以減少讀取容量單位的費用。

對於僅請求索引鍵和投影屬性的索引查詢,DynamoDB 會使用為資料表查詢計算佈建讀取活動相同的方式,計算佈建讀取活動。唯一的差別在於計算方式是以索引項目的大小為基礎,而非基礎資料表中項目的大小。讀取容量單位數為所有傳回項目中,所有投影屬性大小的總和;結果會四捨五入至下一個 4 KB 界限。如需 DynamoDB 如何計算佈建輸送用量的詳細資訊,請參閱 DynamoDB 佈建容量模式

對於讀取未投影到本機次要索引的屬性的索引查詢,除了從索引讀取投影的屬性之外,DynamoDB 還需要從基本資料表擷取這些屬性。當您在 Query 操作的 SelectProjectionExpression 參數中包含任何非投影屬性,即會發生這些擷取。擷取會在查詢回應中造成額外的延遲,也會產生較高的佈建輸送量成本:除了從先前描述的本機次要索引讀取之外,您還需要針對每個擷取的基礎資料表項目支付讀取容量單位費用。此費用是用於讀取資料表中的每個項目,而不僅僅是請求的屬性。

Query 操作傳回的結果大小上限是 1 MB。這包含所有傳回項目之所有屬性名稱與值的大小。不過,如果針對本機次要索引進行查詢導致 DynamoDB 從基礎資料表擷取項目屬性,則結果中資料的大小上限可能會降低。在這種情況下,結果大小是以下各項的總和:

  • 索引中相符項目的大小,且項目大小會四捨五入至下一個 4 KB。

  • 基礎資料表中每個相符項目的大小,且每個項目的大小會四捨五入至下一個 4 KB。

若使用此公式,查詢操作傳回的結果的大小上限仍為 1 MB。

例如,假設有一個資料表,其中每個項目的大小均為 300 個位元組。該資料表上有本機次要索引,但每個項目只有 200 個位元組投影到索引中。現在假設您 Query 此索引,查詢將需為每個項目擷取資料表,且查詢會傳回 4 個項目。DynamoDB 會計算以下總數:

  • 索引中相符項目的大小:200 個位元組 × 4 個項目 = 800 個位元組;接著會四捨五入至 4 KB。

  • 基礎資料表中每個相符項目的大小:(300 個位元組,四捨五入至 4 KB) × 4 個項目 = 16 KB。

因此,結果中的資料總大小為 20 KB。

寫入容量單位

當新增、更新或刪除資料表中的項目時,更新本機次要索引會耗用資料表的佈建寫入容量單位。寫入的總佈建輸送量成本為寫入資料表使用的寫入容量單位,加上更新本機次要索引使用的寫入容量單位。

將項目寫入本機次要索引的成本取決於幾個因素:

  • 若您將項目寫入定義索引屬性的資料表,或是您將現有的項目更新為先前未定義的索引屬性,則將該項目寫入索引需要進行一次寫入操作。

  • 若資料表的更新會變更索引鍵屬性的值 (從 A 到 B),則需要兩次寫入:一次是從索引刪除先前的項目,第二次則是將新的項目寫入索引。 

  • 若項目存在於索引中,但寫入資料表致使索引屬性遭到刪除,則需要進行一次寫入,從索引刪除舊項目的投影。

  • 若項目在更新之前或之後並不存在於索引中,則該索引將不會有任何額外的寫入成本。

所有這些因素都假設索引中每個項目的大小都小於或等於 1 KB 項目大小 (用於計算寫入容量單位)。較大的索引項目需要額外的寫入容量單位。您可以考慮查詢需要傳回的屬性,並只將那些屬性投影到索引中,從而將寫入成本降至最小。

本機次要索引的儲存考量

當應用程式將項目寫入資料表時,DynamoDB 會自動將正確的部分屬性複製到應顯示這些屬性的任何本機次要索引中。 AWS 您的帳戶會針對儲存基礎資料表中的項目,以及儲存該資料表上任何本機次要索引中的屬性付費。

索引項目使用的空間數為下列項目的總和:

  • 基礎資料表主索引鍵 (分割區索引鍵和排序索引鍵) 的大小 (位元組)

  • 索引鍵屬性的大小 (位元組)

  • 投影屬性 (若有的話) 的大小 (位元組)

  • 每個索引項目 100 位元組的額外負荷

若要估算本機次要索引的儲存需求,您可以估算索引中項目的平均大小,再乘以索引中的項目數量。

如果資料表包含特定屬性未經定義的項目,但該屬性卻已定義為索引的排序索引鍵時,DynamoDB 不會將該項目的任何資料寫入索引。

本機次要索引中的項目集合

注意

此節僅適用於具有本機次要索引的資料表。

在 DynamoDB 中,項目集合是資料表及其所有本機次要索引中具有相同分割區索引鍵值的任何項目群組。在本節所使用的範例中,Thread 資料表的分割區索引鍵為 ForumNameLastPostIndex 的分割區索引鍵也為 ForumName。所有具有相同 ForumName 的資料表和索引項目均屬於相同的項目集合。例如,在 Thread 資料表與 LastPostIndex 本機次要索引中,論壇 EC2 有一個項目集合,論壇 RDS 則有一個不同的項目集合。

下圖顯示論壇 S3 的項目集合。

具有資料表和本機次要索引項目的 DynamoDB 項目集合,具有相同的分割區索引鍵值 S3。

在此圖表中,項目集合由 ThreadLastPostIndex 中的所有項目組成,其中 ForumName 分割區索引鍵值為「S3」。如果資料表上有其他本機次要索引,則這些索引中 ForumName 等於「S3」的任何項目也將成為項目集合的一部分。

您可以在 DynamoDB 中使用下列任何操作來傳回項目集合的相關資訊:

  • BatchWriteItem

  • DeleteItem

  • PutItem

  • UpdateItem

  • TransactWriteItems

這些操作中的每一個都支援 ReturnItemCollectionMetrics 參數。在將此參數設定為 SIZE,您可以檢視索引中每個項目集合大小的相關資訊。

範例

以下是 Thread 資料表上 UpdateItem 操作的輸出範例,其中 ReturnItemCollectionMetrics 設定為 SIZE。更新的項目ForumName的值為 "EC2",因此輸出包含有關該項目集合的資訊。

{ ItemCollectionMetrics: { ItemCollectionKey: { ForumName: "EC2" }, SizeEstimateRangeGB: [0.0, 1.0] } }

SizeEstimateRangeGB 物件會顯示此項目集合的大小介於 0 到 1 GB 之間。DynamoDB 會定期更新此大小估計值,因此下次修改項目時,數字可能會有所不同。

項目集合大小限制

包含一或多個本機次要索引之資料表,其任何項目集合的大小上限為 10GB。這不適用於沒有本機次要索引之資料表的項目集合,也不適用於全域次要索引中的項目集合。僅具有一或多個本機次要索引的資料表才會受到影響。

如果項目集合超過 10 GB 的限制,DynamoDB 會傳回 ItemCollectionSizeLimitExceededException,而且您將無法將更多項目新增至項目集合,或增加項目集合中項目的大小。(仍允許會縮小項目集合大小的讀取和寫入操作。) 您仍可將項目新增至其他項目集合。

若要減少項目集合的大小,您可以執行下列操作之一:

  • 刪除有問題的分割區索引鍵值的任何不必要項目。在從基礎資料表中刪除這些項目時,DynamoDB 也會移除具有相同分割區索引鍵值的任何索引項目。

  • 透過移除屬性或減少屬性大小來更新項目。如果這些屬性投影到任何本機次要索引中,DynamoDB 也會減少對應索引項目的大小。

  • 使用相同的分割區索引鍵和排序索引鍵來建立新資料表,然後將項目從舊資料表移至新資料表。如果資料表具有不常存取的歷史資料,這可能是一個很好的方法。您也可以考慮將此歷史資料封存至 Amazon Simple Storage Service (Amazon S3)。

當項目集合的總大小降到 10 GB 以下時,您可以再次新增具有相同分割區索引鍵值的項目。

作為最佳實務,我們建議您檢測應用程式以監控項目集合的大小。方法之一是在使用 BatchWriteItemDeleteItemPutItemUpdateItem 時將 ReturnItemCollectionMetrics 參數設定為 SIZE。您的應用程式應該檢查輸出中的 ReturnItemCollectionMetrics 物件,並在項目集合超過使用者定義的限制 (例如 8 GB) 時記錄錯誤訊息。設定小於 10 GB 的限制會提供一個預警系統,讓您及時掌握項目集合正接近限制,以便採取相關措施。

項目集合和分割區

在包含一或多個本機次要索引的資料表中,每個項目集合會儲存在一個分割區中。此項目集合的總大小僅限於該分割區的容量:10GB。應用程式中,若資料模型包含大小無上限的項目集合,或您可能合理預期某些項目集合日後會超過 10GB,您應考慮改用全域次要索引。

您應將應用程式設計為讓資料表資料均勻分佈在不同的分割區索引鍵值之間。對於具有本機次要索引的資料表,您的應用程式不應在單一分割區上的單一項目集合內建立讀取和寫入活動的「熱點」。