設計適用於 Azure 資料表記憶體的可調整數據分割策略
本文討論在 Azure 數據表記憶體中分割數據表,以及您可以用來確保有效率延展性的策略。
Azure 提供高可用性且高度可調整的雲端記憶體。 Azure 的基礎記憶體系統是透過一組服務提供,包括 Azure Blob 記憶體、Azure 資料表記憶體、Azure 佇列記憶體,以及 Azure 檔案儲存體。
Azure 資料表記憶體是設計來儲存結構化數據。 Azure 記憶體服務支援無限數量的數據表。 每個數據表都可以調整為大規模層級,並提供數 TB 的實體記憶體。 若要充分利用數據表,您必須以最佳方式分割數據。 本文將探索可用來有效率地分割 Azure 數據表記憶體數據的策略。
數據表實體
數據表實體代表儲存在數據表中的數據單位。 數據表實體類似於一般關係資料庫數據表中的數據列。 每一個實體定義一組屬性。 每個屬性都會根據其名稱、值和值的數據類型,定義為索引鍵/值組。 實體的屬性集合中必須定義下列三個系統屬性:
PartitionKey: PartitionKey 屬性會儲存字串值,以識別實體所屬的分割區。 我們稍後討論的數據分割是數據表延展性不可或缺的一部分。 具有相同 PartitionKey 值的實體會儲存在相同的分割區中。
RowKey: RowKey 屬性會儲存可唯一識別每個分割區內實體的字串值。 PartitionKey 和 RowKey 一起形成實體的主鍵。
Timestamp: Timestamp 屬性提供實體的可追蹤性。 時間戳是日期/時間值,會告訴您上次修改實體的時間。 時間戳有時稱為實體 的版本。 系統會忽略時間戳的修改,因為數據表服務會在所有插入和更新作業期間維護此屬性的值。
數據表主鍵
Azure 實體的主鍵是由合併的 PartitionKey 和 RowKey 屬性所組成。 這兩個屬性會形成數據表內的單一叢集索引。 PartitionKey 和 RowKey 值的大小最多可達 1024 個字元。 也允許空字串;不過,不允許 Null 值。
叢集索引會依 PartitionKey 的遞增順序排序,然後依 RowKey 的遞增順序排序。 所有查詢回應中都遵守此排序次序。 排序作業期間會使用語彙比較。 字串值 「111」 會出現在字串值 「2」 之前。 在某些情況下,您可能會希望排序順序為數值。 若要以數值和遞增順序排序,您必須使用固定長度、以零填補的字串。 在上述範例中,“002” 會出現在 “111” 之前。
資料表的資料分割
數據分割代表具有相同 PartitionKey 值的實體集合。 分割區一律會從一部數據分割伺服器提供服務。 每個數據分割伺服器都可以提供一或多個分割區。 對於一個資料分割在一段時間內可提供的實體數量,資料分割伺服器有一定的速率限制。 具體而言,分割區每秒有2000個實體的延展性目標。 在記憶體節點上的最小負載期間,此輸送量可能會較高,但當節點變成經常性存取或作用中時,會進行節流。
為了進一步說明數據分割的概念,下圖顯示一個數據表,其中包含一小部分的數據供腳競爭事件註冊使用。 此圖呈現數據分割的概念檢視,其中 PartitionKey 包含三個不同的值:事件的名稱結合三個距離, (完整 marathon、半 marathon 和 10 km) 。 此範例使用兩部數據分割伺服器。 伺服器 A 包含半 marathon 和 10 公里距離的註冊。 伺服器 B 只包含完整的 marathon 距離。 RowKey 值會顯示為提供內容,但此範例的值並不有意義。
具有三個資料分割的資料表
可擴縮性
因為資料分割一律由單一資料分割伺服器提供,而每一個資料分割伺服器可以提供一或多個資料分割,所以提供實體的效率與伺服器的健康情況有關。 遇到其分割區高流量的伺服器可能無法維持高輸送量。 例如,在上圖中,如果收到 「2011 紐約Marathon__Half」的許多要求,伺服器 A 可能會變得太忙碌。 為了提高伺服器輸送量,儲存系統會將資料分割的負載分攤給其他伺服器。 結果,流量會分散到其他許多伺服器。 為了達到最佳的流量負載平衡,您應該使用更多分割區,讓 Azure 數據表記憶體可以將分割區分散到更多分割伺服器。
實體群組交易
實體群組交易是一組以不可部分完成方式在具有相同 PartitionKey 值的實體上實作的記憶體作業。 如果實體群組中的任何記憶體作業失敗,則會復原實體中的所有記憶體作業。 實體群組交易包含不超過 100 個記憶體作業,且大小可能不超過 4 MiB。 實體群組交易提供 Azure 數據表記憶體,其形式為關係資料庫所提供的不可部分完成性、一致性、隔離和持久性 (ACID) 語意。
實體群組交易可改善輸送量,因為它們會減少必須提交至 Azure 數據表記憶體的個別記憶體作業數目。 實體群組交易也提供經濟優勢。 不論實體群組交易包含多少記憶體作業,都會以單一記憶體作業的形式計費。 由於實體群組交易中的所有記憶體作業都會影響具有相同 PartitionKey 值的實體,因此需要使用實體群組交易可驅動 PartitionKey 值的選取。
範圍分割區
如果您針對實體使用唯一 的 PartitionKey 值,則每個實體都屬於自己的分割區。 如果您使用的唯一值增加或減少值,Azure 可能會建立範圍分割區。 範圍分割區會群組具有循序、唯一 PartitionKey 值的實體,以改善範圍查詢的效能。 如果沒有範圍分割,範圍查詢必須跨分割區界限或伺服器界限,這樣可能會降低查詢效能。 請考慮使用下表的應用程式,其具有 PartitionKey 的遞增序列值:
PartitionKey | RowKey |
---|---|
"0001" | - |
"0002" | - |
"0003" | - |
"0004" | - |
"0005" | - |
"0006" | - |
Azure 可能會將前三個實體分組到範圍分割區。 如果您將範圍查詢套用至使用 PartitionKey 做為準則的數據表,並從 “0001” 要求實體到 “0003”,查詢可能會有效率地執行,因為實體是從單一數據分割伺服器提供。 不保證何時和如何建立範圍分割區。
如果您插入具有增加或減少 PartitionKey 值的實體,數據表的範圍分割可能會影響插入作業的效能。 插入具有增加 PartitionKey 值的實體稱為僅限附加模式。 插入具有遞減值的實體稱為僅限前置模式。 請考慮不使用這類模式,因為插入要求的整體輸送量受限於單一數據分割伺服器。 這是因為,如果範圍分割存在,則第一個和最後一個 (範圍) 分割區分別包含最小和最大的 PartitionKey 值。 因此,插入新的實體,其具有循序較低或更高的 PartitionKey 值,會以其中一個結束分割區為目標。 下圖顯示一組可能以上述範例為基礎的範圍分割區。 如果插入了一組 「0007」、“0008” 和 「0009」 實體,則會指派給最後一個 (橙色) 分割區。
一組範圍分割區
請務必注意,如果插入作業使用更散佈的 PartitionKey 值,則效能不會有任何負面影響。
分析資料
不同於關係資料庫中可用來管理索引的數據表,Azure 數據表記憶體中的數據表只能有一個索引。 Azure 資料表記憶體中的索引一律包含 PartitionKey 和 RowKey 屬性。
在 Azure 數據表中,您不需要藉由新增更多索引,或在推出數據表之後改變現有的數據表,來調整數據表的效能。您必須在設計資料表時分析數據。 為了獲得最佳延展性和查詢和插入效率,要考慮的最重要層面是 PartitionKey 和 RowKey 值。 本文強調如何選擇 PartitionKey ,因為它與數據表的分割方式直接相關。
分割大小
資料分割大小是指資料分割包含的實體數量。 如延 展性所述,擁有更多分割區表示您獲得更好的負載平衡。 PartitionKey 值的數據粒度會影響分割區的大小。 在粗略層級,如果使用單一值做為 PartitionKey,則所有實體都位於非常大的單一分割區中。 在最精細的粒度層級上, PartitionKey 可以包含每個實體的唯一值。 結果是每個實體都有一個分割區。 下表顯示數據粒度範圍的優缺點:
PartitionKey 數據粒度 | 分割區大小 | 優點 | 缺點 |
---|---|---|---|
單一值 | 少量實體 | 任何實體都可能進行批次交易。 所有實體都是本機實體,並從相同的記憶體節點提供服務。 |
|
單一值 | 大量實體 | 任何實體都可以使用實體群組交易。 如需實體群組交易限制的詳細資訊,請參閱 執行實體群組交易。 | 延展性受限。 輸送量受限於單一伺服器的效能。 |
多個值 | 多個分割區 數據分割大小取決於實體散發。 |
某些實體可以進行批次交易。 動態數據分割是可行的。 單一要求查詢可能 (沒有接續令牌) 。 可以跨更多分割區伺服器進行負載平衡。 |
跨分割區的實體分佈高度不平均,可能會限制較大且更作用中分割區的效能。 |
唯一值 | 許多小型分割區 | 數據表可高度擴充。 範圍分割區可能會改善跨分割區範圍查詢的效能。 |
涉及範圍的查詢可能需要造訪多個伺服器。 不支援批次交易。 僅限附加或僅限附加模式可能會影響插入輸送量。 |
下表顯示 PartitionKey 值如何影響縮放比例。 最佳做法是偏好較小的分割區,因為它們提供更佳的負載平衡。 在某些情況下,較大的分割區可能適用,而且不一定是缺點。 例如,如果您的應用程式不需要延展性,則單一大型分割區可能適用。
判斷查詢
查詢會從資料表擷取資料。 當您分析 Azure 資料表記憶體中數據表的數據時,請務必考慮應用程式將使用的查詢。 如果應用程式有數個查詢,您可能必須排定其優先順序,雖然您的決策可能具有主旨。 在許多情況下,主要查詢可以與其他查詢辨識。 在效能方面,查詢分成不同類別。 因為數據表只有一個索引,所以查詢效能通常與 PartitionKey 和 RowKey 屬性有關。 下表顯示不同類型的查詢及其效能評等:
查詢類型 | PartitionKey 比對 | RowKey 比對 | 效能評等 |
---|---|---|---|
資料列範例掃描 | 精確 | 部分 | 較小型的數據分割較佳。 使用非常大型的數據分割時發生錯誤。 |
資料分割範圍掃描 | 部分 | 部分 | 適合使用少量的數據分割伺服器。 更糟的是,有更多伺服器受到觸控。 |
完整資料表掃描 | 部分,無 | 部分,無 | 更糟的是,掃描的數據分割子集。 掃描所有分割區最差。 |
注意
表格中定義彼此相對的效能評等。 分割區的數目和大小最終可能會決定查詢的執行方式。 例如,分割區範圍掃描具有許多大型分割區的數據表,相較於具有少數小型數據分割之數據表的完整數據表掃描,可能會效能不佳。
上表中所列的查詢類型會根據其效能評等,顯示從最佳查詢類型到最差類型的進度。 點查詢是最佳的查詢類型,因為完全使用資料表的叢集索引。 下列點查詢會使用腳部競爭註冊數據表中的數據:
http://<account>.windows.core.net/registrations(PartitionKey=”2011 New York City Marathon__Full”,RowKey=”1234__John__M__55”)
如果應用程式使用多個查詢,則不可能全部都是點查詢。 在效能方面,範圍查詢低於點查詢。 有兩種類型的範圍查詢:數據列範圍掃描和數據分割範圍掃描。 資料列範圍掃描指定單一資料分割。 由於作業發生在單一數據分割伺服器上,因此數據列範圍掃描通常比數據分割範圍掃描更有效率。 不過,數據列範圍掃描效能的一個重要因素是選擇性查詢的效能。 查詢選擇性決定必須逐一查看多少資料列才能找到相符的資料列。 在資料列範圍掃描期間,查詢的選擇性越高,越有效率。
若要評估查詢的優先順序,請考慮每個查詢的頻率和回應時間需求。 經常執行的查詢可能會優先順序較高。 不過,重要但很少使用的查詢可能會有低延遲需求,可能會將它排名在優先順序清單上較高。
選擇 PartitionKey 值
任何數據表設計的核心是其延展性、用來存取它的查詢,以及記憶體作業需求。 您選擇的 PartitionKey 值會決定如何分割數據表,以及您可以使用的查詢類型。 記憶體作業,特別是插入,也可能會影響您選擇的 PartitionKey 值。 PartitionKey 值的範圍可以從單一值到唯一值。 您也可以使用多個值來建立它們。 您可以使用實體屬性來形成 PartitionKey 值。 或者,應用程式可以計算值。 下列各節將討論重要的考慮。
實體群組交易
開發人員應該先考慮應用程式是否會使用實體群組交易 (批次更新) 。 實體群組交易需要實體具有相同 的 PartitionKey 值。 此外,因為批次更新適用於整個群組,所以 PartitionKey 值的選項可能會受到限制。 例如,維護現金交易的銀行業應用程式必須自動將現金交易插入至資料表。 現金交易同時代表轉帳和信用額度,且必須凈為零。 這項需求表示帳戶號碼不能當做 PartitionKey 值的任何部分使用,因為交易的每一端都會使用不同的帳戶號碼。 相反地,交易標識碼可能是更好的選擇。
分割區
分割區編號和大小會影響載入中數據表的延展性。 它們也會受到 PartitionKey 值細微程度所控制。 根據分割區大小來判斷 PartitionKey 可能很困難,特別是當值分佈難以預測時。 根據經驗法則,建議使用多個較小的資料分割。 許多數據表分割區可讓您更輕鬆地管理數據分割所提供之記憶體節點的 Azure 資料表記憶體。
針對 PartitionKey 選擇唯一或更精細的值會導致較小的分割區,但更多數據分割。 這通常很有利,因為系統可以負載平衡許多分割區,以將負載分散到許多分割區。 不過,對於跨資料分割的範圍查詢,您應該考量較多資料分割所產生的效果。 這些類型的查詢必須造訪多個分割區,才能滿足查詢。 分割區可能會分散到許多分割區伺服器。 如果查詢跨越伺服器界限,則必須傳回接續 Token。 接續標記會指定下一個 PartitionKey 或 RowKey 值,以擷取查詢的下一組數據。 換句話說,接續令牌至少代表服務一個以上的要求,這可能會降低查詢的整體效能。
查詢選擇性是另一個會影響查詢效能的因素。 查詢選擇性可測量每一個資料分割必須逐一查看多少資料列。 查詢越有選擇性,查詢在傳回您想要的數據列時更有效率。 範圍查詢的整體效能可能取決於必須接觸的數據分割伺服器數目,或查詢的選擇性程度。 當您將數據插入數據表時,也應該避免使用僅限附加或僅限附加的模式。 如果您使用這些模式,儘管建立小型和許多分割區,您可能會限制插入作業的輸送量。 僅附加和僅限附加模式會在 Range 資料分割中討論。
查詢
瞭解您將使用的查詢可協助您判斷哪些屬性對於 PartitionKey 值而言很重要。 您在查詢中使用的屬性是 PartitionKey 值的候選專案。 下表提供如何判斷 PartitionKey 值的一般指導方針:
如果實體... | 動作 |
---|---|
有一個索引鍵屬性 | 將它當做 PartitionKey 使用。 |
有兩個索引鍵屬性 | 使用一個做為 PartitionKey ,另一個做為 RowKey。 |
有兩個以上的索引鍵屬性 | 使用串連值的複合索引鍵。 |
如果有一個以上的平均主控查詢,您可以使用您需要的不同 RowKey 值多次插入資訊。 您的應用程式會管理次要 (或第三個,依此類) 數據列。 您可以使用這種類型的模式來滿足查詢的效能需求。 下列範例會使用腳部競爭註冊範例中的數據。 它有兩個主要查詢:
- 依 bib 號碼查詢
- 依年齡查詢
為了處理這兩個支配查詢,插入兩個資料列當作實體群組交易。 下表顯示此案例的 PartitionKey 和 RowKey 屬性。 RowKey 值會提供 bib 和 age 的前置詞,讓應用程式可以區分這兩個值。
PartitionKey | RowKey |
---|---|
2011 New York City Marathon__Full | BIB:01234__John__M__55 |
2011 New York City Marathon__Full | AGE:055__1234__John__M |
在此範例中,實體群組交易是可行的,因為 PartitionKey 值相同。 群組交易提供插入作業的不可部分完成性。 雖然此模式可以搭配不同的 PartitionKey 值使用,但建議您使用相同的值來取得這項優點。 否則,您可能必須撰寫額外的邏輯,以確保使用不同 PartitionKey 值的不可部分完成交易。
儲存體作業
Azure 資料表記憶體中的數據表可能不只會從查詢載入。 它們也可能會遇到來自記憶體作業的負載,例如插入、更新和刪除。 請考慮您要在數據表上執行的記憶體作業類型,以及速率為何。 如果您不常執行這些作業,您可能不需要擔心這些作業。 不過,對於像是在短時間內執行許多插入的頻繁作業,您必須考慮這些作業如何作為您選擇的 PartitionKey 值結果。 重要範例是僅限附加和僅限附加的模式。 僅附加和僅限附加模式會在 Range 資料分割中討論。
當您使用僅限附加或僅限附加的模式時,會在後續插入時,針對 PartitionKey 使用唯一的遞增或遞減值。 如果您結合此模式與頻繁的插入作業,您的數據表將無法以絕佳的延展性來服務插入作業。 數據表的延展性受到影響,因為 Azure 無法對其他分割伺服器進行作業要求的負載平衡。 在此情況下,您可能想要考慮使用隨機值,例如 GUID 值。 然後,分割區大小可以保持小,但仍會在記憶體作業期間維持負載平衡。
數據表分割壓力測試
當 PartitionKey 值很複雜或需要與其他 PartitionKey 對應的比較時,您可能需要測試數據表的效能。 此測試應該檢查資料分割在尖峰負載下的表現。
執行壓力測試
- 建立測試資料表
- 使用數據載入測試數據表,使其包含具有您目標 PartitionKey 值的實體。
- 使用應用程式模擬數據表的尖峰負載。 使用步驟 2 中的 PartitionKey 值,以單一分割區為目標。 此步驟對於每個應用程式而言都不同,但模擬應該包含所有必要的查詢和記憶體作業。 您可能需要調整應用程式,使其以單一分割區為目標。
- 檢查 GET 或 PUT 作業在資料表上的輸送量。
若要檢查輸送量,請比較實際值與單一伺服器上單一資料分割的指定限制。 分割區每秒限制為2000個實體。 如果磁碟分區的輸送量超過每秒 2000 個實體,伺服器可能會在生產設定中執行太熱。 在此情況下, PartitionKey 值可能太粗略,因此沒有足夠的數據分割或分割區太大。 您可能需要修改 PartitionKey 值,讓分割區分散到更多伺服器。
負載平衡
分割區層的負載平衡會在分割區太忙碌時發生。 當分割區太忙碌時,分割區特別是分割區伺服器會超出其目標延展性。 針對 Azure 記憶體,每個分割區每秒都有 2000 個實體的延展性目標。 負載平衡也會發生在分散式文件系統 (DFS) 層。
DFS 層的負載平衡會處理 I/O 負載,並超出本文的範圍。 分割層的負載平衡不會在超過延展性目標之後立即發生。 相反地,系統會在開始負載平衡程式之前等候幾分鐘。 這樣可確定資料分割已確實變得忙碌。 由於系統會自動執行工作,因此不需要以產生負載來觸發負載平衡的質化數據分割。
如果數據表是使用特定負載來準備,系統可能會根據實際負載來平衡分割區,這會導致分割區分佈明顯不同。 請考慮撰寫處理逾時和伺服器忙碌錯誤的程式代碼,而不是預備數據分割。 當系統進行負載平衡時,會傳回錯誤。 藉由使用重試策略來處理這些錯誤,您的應用程式可以更妥善地處理尖峰負載。 下一節詳細討論重試策略。
發生負載平衡時,分割區會脫機幾秒鐘。 在離線期間,系統會將分割區重新指派給不同的分割區伺服器。 請務必注意,您的數據不會由數據分割伺服器儲存。 反之,資料分割伺服器會從 DFS 層提供實體。 因為您的數據不會儲存在數據分割層,所以將數據分割移至不同的伺服器是一個快速的程式。 此彈性可大幅限制您的應用程式可能會遇到的停機時間。
重試策略
您的應用程式必須處理記憶體作業失敗,以確保您不會遺失任何數據更新。 某些失敗不需要重試策略。 例如,傳回 401 未經授權錯誤的更新無法受益於重試作業,因為應用程式狀態可能不會在解決 401 錯誤的重試之間變更。 不過,伺服器忙碌或逾時之類的錯誤與 Azure 的負載平衡功能相關,可提供數據表延展性。 當服務實體的記憶體節點變成經常性存取時,Azure 會藉由將分割區移至其他節點來平衡負載。 在此期間,分割區可能會無法存取,這會導致伺服器忙碌或逾時錯誤。 最後,磁碟分區會重新啟用,並繼續更新。
重試策略適用於伺服器忙碌或逾時錯誤。 在大部分情況下,您可以從重試邏輯中排除 400 個層級的錯誤和一些 500 層級的錯誤。 可排除的錯誤包括 501 未實作和不支援 505 HTTP 版本。 然後,您可以針對最多 500 個層級的錯誤實作重試策略,例如伺服器忙碌 (503) 和逾時 (504) 。
您可以選擇應用程式的三個常見重試策略:
- 無重試:未嘗試重試。
- 已修正輪詢:作業會重試 N 次,並具有常數輪詢值。
- 指數輪詢:作業會以指數輪詢值重試 N 次。
「不重試」策略是處理作業失敗的一種簡單 (規避) 的方法。 不過,沒有重試策略並不實用。 不強制任何重試很可能在失敗作業之後無法正確儲存資料。 更好的策略是使用固定輪詢策略。 可讓您使用相同的輪詢持續時間重試作業。
不過,策略並未針對處理可高度擴充的數據表進行優化。 如果許多線程或進程正在等候相同的持續時間,可能會發生衝突。 建議的重試策略是使用指數輪詢的重試策略,其中每個重試嘗試的時間超過最後一次嘗試的時間。 這類似於計算機網路中使用的衝突避免演算法,例如乙太網路。 指數型輪詢使用隨機因子來提供結果間隔的額外變化。 然後將輪詢值限制在上下限之間。 下列公式採用指數型演算法來計算下一個輪詢值:
y = Rand(0.8z, 1.2z)(2x-1
y = Min(zmin + y, zmax
其中:
z = 預設輪詢 (以亳秒為單位)
zmin = 預設最小輪詢 (以亳秒為單位)
zmax = 預設最大輪詢 (以亳秒為單位)
x = 重試次數
y = 輪詢值 (以亳秒為單位)
Rand (隨機) 函式中使用的 0.8 和 1.2 乘數會產生原始值±20% 內預設輪詢的隨機變異數。 大部分重試策略都可以接受 ±20% 的範圍,而且可防止進一步的衝突。 您可以使用下列程式代碼來實作公式:
int retries = 1;
// Initialize variables with default values
var defaultBackoff = TimeSpan.FromSeconds(30);
var backoffMin = TimeSpan.FromSeconds(3);
var backoffMax = TimeSpan.FromSeconds(90);
var random = new Random();
double backoff = random.Next(
(int)(0.8D * defaultBackoff.TotalMilliseconds),
(int)(1.2D * defaultBackoff.TotalMilliseconds));
backoff *= (Math.Pow(2, retries) - 1);
backoff = Math.Min(
backoffMin.TotalMilliseconds + backoff,
backoffMax.TotalMilliseconds);
摘要
Azure 資料表記憶體中的應用程式可以儲存大量數據,因為資料表記憶體會管理及重新指派多個記憶體節點之間的分割區。 您可以使用資料分割來控制資料表的延展性。 在您定義數據表架構時事先規劃,以確保您實作有效率的數據分割策略。 具體而言,請先分析應用程式的需求、數據和查詢,再選取 PartitionKey 值。 當系統回應流量時,每個分割區可能會重新指派給不同的記憶體節點。 使用數據分割壓力測試來確保數據表具有正確的 PartitionKey 值。 此測試可協助您判斷分割區何時太熱,並協助您進行必要的分割區調整。
為了確保您的應用程式會處理間歇性錯誤,並保存您的數據,請使用重試策略搭配輪詢。 Azure 記憶體用戶端連結庫使用的預設重試原則具有指數輪詢,可避免衝突,並將應用程式的輸送量最大化。