匯出 (0) 列印
全部展開

為 Azure 資料表儲存體設計可擴充的資料分割策略

更新日期: 2014年8月

作者: https://msdn.microsoft.com/zh-tw/library/hh307529

參考影像

深入了解 RBA 諮詢

摘要 本文討論 Azure 資料表分割的相關主題,以及用以確保有效延展性的策略。

Azure 提供可靠的資料儲存體,不但隨時可用,且延展性極高。Azure 的基礎儲存系統是透過一組稱為服務的物件抽象概念來提供,這些服務由資料表、Blob 及佇列服務組成。每一項服務可以滿足您的大部分開發需求。當您的應用程式需求儲存結構化資料時,Azure 資料表是您的最佳選擇。Azure 儲存服務支援數量不限的資料表,每個資料表可以延展至大量層級。資料表可以向外展延來儲存相當於數 TB 實體儲存的數百個實體。為了充分發揮資料表的優點,應該使用最佳的 PartitionKey 值來分割資料。本文探討將 Azure 資料表儲存體的資料有效率地分割的策略。

資料表實體代表資料表中儲存的資料單位,類似於一般關聯式資料庫資料表中的資料列。每一個實體定義一組屬性。每一個屬性是一對機碼/值組,以名稱、值和值的資料類型來定義。實體的屬性集合中必須定義下列三個系統屬性:

  • PartitionKey – PartitionKey 屬性儲存的字串值可識別實體所屬的資料分割。這表示具有相同 PartitionKey 值的實體屬於相同資料分割。如稍早所述,資料分割是資料表延展性所不可或缺的。

  • RowKey – RowKey 屬性儲存的字串值可唯一地識別每一個資料分割內的實體。

  • Timestamp – Timestamp 屬性提供實體的可追蹤性。時間戳記是 DateTime 值,指出實體上次修改的時間。時間戳記有時稱為實體的版本。系統會忽略時間戳記的修改,因為在所有插入和更新作業期間,資料表服務會維護此屬性的值。

任何資料庫資料表的主索引鍵會定義資料行來唯一地定義每一個資料列。Azure 資料表也是如此。Azure 資料表的主索引鍵是 PartitionKey 和 RowKey 屬性,形成資料表內的單一叢集索引。每一個 PartitionKey 和 RowKey 屬性最多可儲存 1 KB 的字串值。允許空字串,但不允許空值。叢集索引先依 PartitionKey 的遞增順序排序,再依 RowKey 的遞增順序排序。所有查詢回應中都遵守此排序次序。排序作業期間會使用語彙比較。因此,字串值 "111" 會出現在字串值 "2" 之前。在某些情況下,您可能想要使用數值次序。若要依數值遞增次序來排序,您必須使用固定長度、填補零的字串。在上一個範例中,使用 "002" 可讓它在 "111" 之前出現。

資料分割代表具有相同 PartitionKey 值的一組實體。資料分割一律由一個資料分割伺服器提供,而每一個資料分割伺服器可以提供一或多個資料分割。對於一個資料分割在一段時間內可提供的實體數量,資料分割伺服器有一定的速率限制。具體而言,資料分割的延展性目標為每秒 500 個實體。此輸送量在儲存節點負載最低時可能很高,但在儲存節點變得非常忙碌時就會降低速度。為了充分說明資料分割的概念,下圖有一個資料表包含賽跑活動記登的一小部分資料。它呈現資料分割的概念圖,PartitionKey 包含三個不同的值,這些值由活動名稱和距離組成。在此範例中,有兩個資料分割伺服器。伺服器 A 包含半程馬拉松和 10 Km 距離的登記,伺服器 B 只包含全程馬拉松距離。RowKey 值在此是為了配合說明,對此範例而言沒有意義。

參考畫面

因為資料分割一律由單一資料分割伺服器提供,而每一個資料分割伺服器可以提供一或多個資料分割,所以提供實體的效率與伺服器的健康情況有關。如果伺服器的資料分割面臨很高的流量,可能無法承受太高的輸送量。例如,在上圖中,如果 "2011 New York City Marathon__Half" 的要求太多,伺服器 A 可能會變得太忙碌。為了提高伺服器輸送量,儲存系統會將資料分割的負載分攤給其他伺服器。結果,流量會分散到其他許多伺服器。為了使流量達到最佳的負載平衡,您應該使用更多資料分割,因為這樣可讓 Azure 資料表服務將資料分割分配至更多資料分割伺服器。

實體群組交易是一組儲存作業,在具有相同 PartitionKey 值的實體上會自動實作。只要實體群組中有任何儲存作業失敗,則其中的所有儲存作業都會回復。實體群組交易由不超過 100 個儲存作業組成,且大小不可超過 4 MB。實體群組交易為 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 資料表只能有一個索引,一律由 PartitionKey 和 RowKey 屬性組成。在發佈資料表之後,您就不可能有時間加入更多索引或改變現有的索引來調整資料表的效能。因此,設計資料表時必須分析資料。為了達到最佳的延展性及查詢和插入效率,PartitionKey 和 RowKey 值是最重要的兩個考量因素。本文在如何選擇 PartitionKey 方面多有著墨,因為這直接關係到資料表如何分割。

資料分割大小是指資料分割包含的實體數量。「延展性」一節已提過,資料分割越多代表負載平衡越好。PartitionKey 值的細緻性會影響資料分割的大小。以最粗略的程度而言,如果使用單一值作為 PartitionKey,則所有實體都在單一、極大的資料分割中。反之,以最細緻的程度而言,PartitionKey 可以包含每一個實體的唯一值。最後是每一個實體各有一個資料分割。下表顯示細緻性範圍的優缺點。

 

PartitionKey 細緻性

資料分割大小

優點

缺點

單一值

少量實體

任何實體可支援批次交易

所有實體都在本機並由相同儲存節點提供

 

單一值

大量實體

任何實體可支援實體群組交易。如需實體群組交易限制的詳細資訊,請參閱 http://msdn.microsoft.com/en-us/library/dd894038.aspx

延展性受限。

輸送量受限於單一伺服器的效能。

多個值

有多個資料分割

資料分割大小視實體分布情況而定

部分實體上可執行批次交易

支援動態資料分割

支援單一要求查詢 (無接續 Token)

支援多個資料分割伺服器之間的負載平衡

實體在資料分割之間分布極不平均可能使較大和較忙碌的資料分割的效能受限。

唯一值

有許多小型資料分割。

資料表延展性較高

界定資料分割可改進跨資料分割範圍查詢的效能

涉及範圍的查詢可能需要存取多個伺服器。

不支援批次交易。

僅附加或僅前面加上的模式會影響插入作業的輸送量

此表格顯示 PartitionKey 值如何影響延展性。最好採用較小的資料分割,因為負載平衡效果較好。較大的資料分割在某些情況下可能適合,並不一定都有害處。例如,假設應用程式不需要延展性,則單一大型資料分割就可能適合。

查詢會從資料表擷取資料。當您分析 Azure 資料表的資料時,必須考慮應用程式將使用哪些查詢。如果應用程式有多個查詢,可能需要排好優先順序,即使您的決定可能有些主觀。在許多情況下,支配查詢明顯有別於其他查詢。在效能方面,查詢分成不同類別。因為資料表只有一個索引,所以查詢效能通常與 PartitionKey 和 RowKey 屬性有關。下表顯示不同類型的查詢及其效能評等。

 

查詢類型

PartitionKey 符合

RowKey 符合

效能評等

Point

完全

完全

最佳

資料列範例掃描

完全

部分

小型資料分割較佳

大型資料分割較佳

資料分割範圍掃描

部分

部分

存取較少資料分割伺服器時良好

存取較多伺服器時較差

完整資料表掃描

部分,無

部分,無

掃描一部分的資料分割時較差

掃描所有資料分割時最差

note附註
表格中定義彼此相對的效能評等。資料分割的數量和大小最後可能決定查詢如何執行。例如,相較於在只有少數小型資料分割的資料表上進行完整資料表掃描,在有許多大型資料分割的資料表上進行資料分割範圍掃描,效率可能很差。

此表格列出的查詢類型根據效能評等,依序顯示從使用最佳到最差的查詢類型。點查詢是最佳的查詢類型,因為完全使用資料表的叢集索引。下列點查詢使用賽跑登記資料表的資料。

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 可能不容易,尤其是在值的分佈情況難以預測時更是如此。根據經驗法則,建議使用多個較小的資料分割。較多的資料表資料分割可讓 Azure 資料表服務輕鬆地管理負責提供資料分割的儲存節點。

選擇唯一或較精細的值作為 PartitionKey 會產生較小、較多的資料分割。這通常很適合,因為系統可以平衡許多資料分割的負載,將負載分散至許多資料分割。不過,對於跨資料分割的範圍查詢,您應該考量較多資料分割所產生的效果。這種查詢必須存取多個資料分割才能滿足查詢。資料分割可能分布在許多資料分割伺服器上。如果查詢跨越伺服器界限,則必須傳回接續 Token。接續 Token 指定下一個 PartitionKey 或 RowKey 值,用以擷取查詢的下一組資料。換言之,接續 Token 代表對服務的至少一或多個要求,可能降低查詢的整體效能。查詢選擇性是另一個會影響查詢效能的因素。查詢選擇性可測量每一個資料分割必須逐一查看多少資料列。查詢的選擇性越高,傳回所需資料列的效率越高。範圍查詢的整體效能可能取決於必須存取的資料分割伺服器數量,或查詢的選擇性高低。將資料插入至資料表時,您也應該避免使用僅附加或僅前面加上的模式。就算是建立較小的許多資料分割,使用這種模式會限制插入作業的輸送量。「界定資料分割」小節中討論僅附加和僅前面加上的模式。

知道您將使用的查詢可讓您決定必須考量哪些屬性作為 PartitionKey。查詢中使用的屬性都是 PartitionKey 的候選項目。下表提供如何決定 PartitionKey 的一般指導方針。

 

如果實體...

Action

有一個索引鍵屬性

作為 PartitionKey

有兩個索引鍵屬性

一個作為 PartitionKey,另一個作為 RowKey

有兩個以上的索引鍵屬性

使用連串值的複合索引鍵

如果有多個同等的支配查詢,您可以用您需要的不同 RowKey 值來插入資訊多次。第二個 (或第三個等) 資料列由您的應用程式管理。此模式可讓您滿足查詢的效能需求。下列範例使用賽跑登記範例的資料。它有兩個支配查詢。它們是:

  • 依 bib 號碼查詢

  • 依年齡查詢

為了處理這兩個支配查詢,插入兩個資料列當作實體群組交易。下表顯示此案例的 Partitionkey 和 RowKey 屬性。RowKey 值提供 bib 和年齡的前置詞,讓應用程式可以區別這兩個值。

 

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 值來使用此模式,但建議您使用相同的值來利用這項好處。否則,您必須撰寫額外的邏輯,以確保具有不同 PartitonKey 值的交易不可部分完成。

Azure 資料表不只會遇到來自查詢的負載,也有來自儲存作業的負載,例如插入、更新和刪除。您必須考量您會在資料表上執行何種儲存作業及執行的頻繁次數。如果您不常執行這些作業,可能就不需要擔心。不過,對於很經常執行的作業,例如在短時間內執行許多插入作業,則必須考量您選擇的 PartitionKey 值如何影響這些作業的處理。一個重要的例子是僅附加或僅前面加上模式。先前的「界定資料分割」小節已討論過這些模式。使用僅附加或僅前面加上模式時,表示您在後續的插入作業中使用唯一的遞增或遞減值作為 PartitionKey。如果結合此模式與經常的插入作業,則資料表無法以很高的延展性來處理插入作業。資料表的延展性會受影響,因為 Azure 無法將作業要求的負載平均分攤至其他資料分割伺服器。因此,在此情況下,您可以考慮使用隨機的值,例如 GUID 值。這可讓您的資料分割大小在儲存作業期間仍然保持很小,同時維持負載平衡。

當 PartitionKey 值很複雜或需要與其他 PartitionKey 對應比較時,您可能需要測試資料表的效能。此測試應該檢查資料分割在尖峰負載下的表現。

執行壓力測試

  1. 建立測試資料表

  2. 在測試資料表中載入資料,使之包含實體及您將挑選作為目標的 PartitionKey。

  3. 利用應用程式來模擬資料表的尖峰負載,並利用步驟 2 取得的 PartitionKey 來以單一資料分割為目標。此步驟在每個應用程式中都不同,但模擬應該包含所有必要的查詢和儲存作業。應用程式可能需要調整才能挑出單一資料分割作為目標。

  4. 檢查 GET 或 PUT 作業在資料表上的輸送量。

若要檢查輸送量,請比較實際值與單一伺服器上單一資料分割的指定限制。資料分割以每秒 500 個實體為限。如果資料分割的輸送量超過每秒 500 個實體,表示在生產設定下,伺服器可能太忙碌。在此情況下,PartitionKey 值可能太粗略,以致於資料分割不足或資料分割太大。您可能需要修改 PartitionKey 值,使資料分割可以分布在更多伺服器之間。

當資料分割太忙碌時,資料分割層上會發生負載平衡,這表示資料分割 (特別是資料分割伺服器) 運作已超出目標延展性。以 Azure 儲存體而言,每一個資料分割的延展性目標為每秒 500 個實體。負載平衡也會發生在分散式檔案系統層 (簡稱為 DFS 層)。DFS 層的負載平衡與 I/O 負載有關,超出本文的範疇。在超出延展性目標之後,資料分割層的負載平衡不會立即發生。反之,系統會等待幾分鐘之後,才開始負載平衡程序。這樣可確定資料分割已確實變得忙碌。不需要以產生的負載來活絡資料分割以觸發負載平衡,因為系統會自動執行此工作。如果資料表忙於處理某些負載,系統會根據實際的負載來平衡資料分割,使資料分割形成極為不同的分布。您應該考慮撰寫程式碼來處理「逾時」和「伺服器忙碌中」錯誤,而不是忙於活絡資料分割。當系統執行負載平衡時會傳回這些錯誤。採用重試策略來處理這些錯誤,您的應用程式可以更妥善處理尖峰負載。下一節詳細討論重試策略。負載平衡發生時,資料分割會有秒鐘處於離線狀態。在離線期間,系統會將資料分割重新指派給不同的資料分割伺服器。必須注意,資料分割伺服器不會儲存您的資料。反之,資料分割伺服器會從 DFS 層提供實體。因為您的資料不儲存在資料分割層,很快就能將資料分割移至不同的伺服器。這樣可大幅縮短應用程式可能遭遇的停機時間 (若發生的話)。

您的應用程式必須處理儲存作業失敗,以確保您不會遺失任何資料更新。某些失敗不需要用到重試策略。例如,會傳回「401 未經授權」錯誤的更新作業無法從重試作業中獲益,因為在最後會解決 401 錯誤的每次重試之間,應用程式狀態可能不會改變。不過,某些錯誤 (例如「伺服器忙碌中」或「逾時」) 與提供資料表延展性的 Azure 負載平衡功能有關。當提供實體的儲存節點變得忙碌時,Azure 會將資料分割移至其他節點來平衡負載。在這段期間,可能無法存取資料分割,因而產生「伺服器忙碌中」或「逾時」錯誤。最後,資料分割會重新啟用,而更新作業也會繼續。重試策略適用於忙碌和逾時錯誤。在大多數情況下,您可以從重試邏輯中排除 400 等級錯誤和一些 500 等級錯誤,例如「501 尚未建置」或「505 HTTP 不支援版本」,並對一些 500 等級錯誤實作重試策略,例如「伺服器忙碌中 (503)」和「逾時 (504)」。

有三種常用的重試策略可用於您的應用程式。以下是這些重試策略的清單及其描述:

  • 不重試 – 不嘗試進行任何重試。

  • 固定輪詢 – 依固定的輪詢值來重試作業 N 次

  • 指數型輪詢 – 依指數型輪詢值來重試作業 N 次

「不重試」策略是處理作業失敗的一種簡單 (規避) 的方法。但不是非常實用。不強制任何重試很可能在失敗作業之後無法正確儲存資料。因此,較佳的策略是使用「固定輪詢」策略,以相同的輪詢持續時間來重試作業。不過,此策略並非最適合用來處理延展性很高的資料表,因為如果相同期間內有許多執行緒或處理序在等待,則可能會發生衝突。建議的重試策略是使用指數型輪詢,也就每一次重試都比上一次嘗試更久。這類似於電腦網路 (例如乙太網路) 中使用的避免碰撞 (CA) 演算法。指數型輪詢使用隨機因子來提供結果間隔的額外變化。然後將輪詢值限制在上下限之間。下列公式採用指數型演算法來計算下一個輪詢值:

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 Managed 程式庫來開發應用程式,您可以利用儲存體用戶端程式庫中包含的重試原則。程式庫中的重試機制也可讓您以自訂的重試原則來擴充功能。Microsoft.WindowsAzure.StorageClient 命名空間中的 RetryPolicies 類別提供可傳回 RetryPolicy 物件的靜態方法。RetryPolicy 物件與 TableServiceContext 類別中的 SaveChangesWithRetries 方法一起使用。TableServiceContext 物件使用的預設原則是 RetryExponential 類別的執行個體 (以 RetryPolicies.DefaultClientRetryCount 和 RetryPolicies.DefaultClientBackoff 值所建構)。下列程式碼顯示如何以不同的 RetryPolicy 來建構 TableServiceContext 類別。

class MyTableServiceContext : TableServiceContext
{
    public MyTableServiceContext(string baseAddress, CloudStorageAccount account)
        : base(baseAddress, account)
    {
        int retryCount = 5; // Default is 3
        var backoff = TimeSpan.FromSeconds(45); // Default is 30 seconds

        RetryPolicy = RetryPolicies.RetryExponential(retryCount, backoff);
    }
    ...
}

Azure 資料表儲存體可讓應用程式儲存大量資料,因為它會管理並重試指派許多儲存節點之間的資料分割。您可以使用資料分割來控制資料表的延展性。當您定義資料表的結構描述時,請事先規劃,以確保資料分割策略的效率。具體而言,在選取 PartitionKey 值之前,請分析應用程式的需求、資料及查詢。隨著系統對流量作出反應,每一個資料分割可重新指派至不同的儲存節點。使用資料分割壓力測試來確保資料表有正確的 PartitionKey 值。此測試可讓您判斷資料分割何時太忙碌,並做出必要的資料分割調整。為了確保應用程式可處理間歇性錯誤並保留您的資料,應該使用重試策略與輪詢。Azure 儲存體用戶端使用的預設重試原則是具有指數型輪詢的原則,可避免衝突並使應用程式達到最大的輸送量。

顯示:
© 2015 Microsoft