Sharding模式

將數據存儲爲一組水平的數據分區。這種模式可以在存儲和訪問大量的數據的時候提高可擴展性。

場景和問題

由單個服務器託管的數據存儲可能受到下列限制:

  • 存儲空間限制。基於大規模雲應用所使用的數據倉庫,可能會包含海量的數據,並且數據增長速度非常的快。一個服務器通常只提供有限容量的磁盤存儲,當然它可能是可以替換現有的磁盤與較大的,或添加進一步磁盤到服務器以增加存儲量。但是,該系統將最終達到一個硬件的限制,一味地增加在服務器上的存儲容量是不可能的。
  • 計算資源限制。雲應用程序需要來支持大量的併發用戶,每個用戶都會從數據存儲中檢索信息。託管數據存儲的單個服務器可能無法提供支持該負載所需的計算能力,從而導致用於用戶的響應時間的延長以及當嘗試存儲和檢索數據的由於超時所產生的頻繁故障。 可以通過添加存儲器或升級處理器來緩解計算資源不足的問題,但是當不可能進一步增加計算資源時,系統還會碰到資源不足的問題。
  • 網絡帶寬限制。最終,在單個服務器上運行的數據存儲的性能還會受到服務器可以接收請求和發送回覆的速度的限制。 可能會因爲網絡流量超過用於連接到服務器的網絡的容量限制,導致請求失敗。
  • 地理位置的限制。可能處於合法或者性能(減少數據訪問的延遲)等原因,需要將特定用戶在同一區域中生成的數據存儲在同一區域。如果用戶分散在不同的國家或地區,可能無法將整個數據存儲在單個數據存儲區中。

通過添加更多的磁盤容量、處理能力、內存和網絡連接來垂直縮放可能會推遲這些限制的影響,但它只是一個臨時的解決方案。一個商業雲應用程序能夠支持大量的用戶和大量的數據必須能夠無限規模地擴展,所以垂直縮放不一定是最好的解決方案。

解決方案

將數據倉庫進行分隔來變成多個水平的分區。每個分區(shard)都有相同的結構,但有其自身不同的數據子集。碎片本身就是一個數據倉庫(可以包含許多不同種類的實體的數據),在服務器上作爲存儲節點運行。

這種模式提供以下好處:

  • 開發者可以通過增加額外的存儲節點來擴展系統。
  • 系統可以使用現成的硬件,而不爲每個存儲節點配置專門的(和昂貴的)計算機。
  • 開發者可以通過平衡不同分片的負載來減少爭用和提高性能。
  • 在雲中,分區可以在地理位置上接近訪問數據的用戶。

當將數據存儲進行分隔成分區時,需靠考慮將哪些數據應該放在哪個分區。一個分區通常需要檢索一個或者多個一定範圍內的數據屬性,就由這些屬性構成Shard的Key(有時稱爲分區Key)。Shard的Key應該是靜態的。它不應該基於可能改變的數據。

分區在物理存儲上組織數據。當一個應用程序存儲和檢索數據的時候,邏輯的分片指向應用到相應的分區。其實分區邏輯是作爲應用數據訪問代碼的一部分來實現的,當然,如果數據存儲系統支持透明的sharding的話,也可以由數據存儲系統來實現。

在sharding邏輯中抽象數據的物理位置可以對那個分區包含哪些數據提供一種high-level的控制,同時,使數據在分區之間遷移的時候不需要重新執行應用的業務邏輯(比如分區數據不平衡的時候,進行的重新分佈)。當然,其代價是在確定每個數據項的位置時所需的額外數據訪問開銷。

爲了確保最佳的性能和可擴展性,根據應用所執行查詢的方式來進行選擇合適的分割數據的方案是非常重要的。在許多情況下,是不太可能令分區方案將完全匹配的每個查詢的要求。例如,在多租戶系統中,應用程序可能需要使用租戶ID來檢索租戶數據,但也可能需要根據其他屬性(如租戶的名稱或位置)來查找這些數據。爲了處理這類情況,就需要設計一種合適的Shard key來滿足最常執行的請求(最關鍵的,對性能要求高CRUD操作)。

如果查詢定期檢索數據結合使用的屬性值,可以通過定義組合屬性爲shard key。另外,使用Index-Table模式可以根據沒有被Shard Key覆蓋的數據進行快速查找。

Sharding策略

通常在選擇shard key和決定如何將數據存入分區使用三種策略。需要提一點的是,分區和對應的服務器是不必一一對應的,服務器可以host多個分區。策略如下:

  • 查找策略。在查找策略中,Sharding的邏輯實現了一個請求到包含指定數據分片的路由映射,該映射是通過shard key來計算的。舉個例子,在一個租房應用中,承租人通過他們的Id作爲shard key來分放在不同的分區。多個承租人信息可能在同一個分區中,但是一個承租人的信息是不會跨越多個分區的。圖1顯示了這個策略的例子。
    這裏寫圖片描述
    圖1.基於租戶ID對租戶信息進行Sharding
    Shard key和物理存儲之間的映射是基於物理的分區的,每個Shard key映射到一個物理分區。另外,有一種技術虛擬分區技術,在平衡物理分區上提供了更多的靈活性。該技術將每一個shard key映射到一個虛擬的分區上,然後將虛擬的分區映射到少量實際的物理分區。在這種方法中,應用程序通過Shard key定位到虛擬分區來獲取對應的數據,然後系統會透明的由虛擬分區映射到物理分區。這樣,虛擬分區和物理分區之間的映射可以在不修改應用代碼的情況下修改,這樣,當應用使用不同的shard key也可以不用修改應用代碼。
  • 範圍策略。範圍策略會將一些相關的數據放在同一個分區之中,然後通過shard key進行排序,shard key必須是連續的。
    在範圍策略中,在應用程序經常進行範圍操作十分高效(根據一個shard key返回落在給定範圍內的一組數據的查詢)。舉例來說,如果應用定期需要查找所有指定月份的訂單,那麼,一旦所有需要獲取的數據都放到同一個分區內,性能就會高很多。如果每個訂單都在不同的分區,就需要執行大量的查詢操作來檢索全部數據了(每個請求返回一個數據項)。圖2展示了範圍策略的例子。
    這裏寫圖片描述
    圖2.存儲連續的數據集合到分區中
    在上面的例子中,shard key是一個組合鍵包括以月份爲最重要的元素,其次是訂單時間。訂單數據在增加新的數據到分區中時,自然就是排好序的。一些數據存儲支持Shard key包括分區的Key用來確認分區,一個列Key來區分分區中的數據。數據在分片中通常是根據列Key來進行排序的。需要支持範圍查詢的數據需要被放入同一個分區,由應用可以根據Shard key來檢索所在的分區,然後根據列key來區分分區中的數據。
  • 哈希策略。哈希策略的目的是爲了減少產生熱點的概率。主要是爲了將數據儘可能的平均分配到各個分區,以保證不同分區的負載均衡。Sharding邏輯會根據存儲數據中的一些屬性來計算一個哈希值。選擇的哈希函數應該儘可能均分數據到各個分區。圖3顯示了哈希策略的例子。
    這裏寫圖片描述
    圖3.基於租戶ID對租戶信息進行分區
    爲了理解哈希策略比其他分區策略的優勢,考慮一個租房應用連續登記了新的租戶,並且將租戶信息存儲到分區中。當使用了範圍分區策略時,租戶ID從1到n將會存儲到Shard A當中,租戶ID從n+1到m會存儲到Shard B當中,以此類推。通常來說最近註冊的租戶也是最爲活躍的,那麼基於範圍策略進行Sharding就會產生熱點。然而,哈希策略會將租戶信息基於租戶ID來寫入分區。這也意味着最新註冊的租戶會寫入不同的分區,如上圖中的租戶55和租戶56,這樣可以很好的均衡負載。

下表列出了三種策略的優勢以及需要考慮的問題:

策略 優勢 需要考慮的信息
查找策略 對於分區的配置和使用控制粒度更細。
使用虛擬分區可以減少重新均衡數據所帶來的一些問題。因爲新的物理分區可以添加到負載中去。虛擬分區和物理分區之間的映射可以在不影響應用代碼的情況下進行修改,完全不會影響到應用通過shard key對數據的存儲和獲取
定位數據所在的分區會帶來額外的計算負載
範圍策略 易於實現,並且在範圍查詢的時候性能很好,因爲所獲取的數據通常都來自於同一個分區,只一次IO就可以獲取全部的數據
易於管理數據。例如,如果在同一區域的用戶都在同一個分區,更新可以安排每個時區基於本地負載和需求自由定製。
無法針對分區之間的負載差異進行優化。
平衡分區很困難,並且在大量請求都是基於臨近行爲的時候,很有可能無法解決負載不均衡的問題。
哈希策略 可以更好的平均分配數據,均衡負載。請求路由可以直接通過哈希函數來實現,無需維護請求到分區的映射。 計算哈希值會帶來額外的計算負載。
重新平衡分區十分困難。

最常見的Sharding的方式,就是使用前文所描述的一些方法,但是開發者也需要同時考慮應用的業務需求以及數據的使用模式。還是以租房應用爲例:

  • 開發者應該根據數據的負載來進行分片。開發者可以將頻繁訪問,較爲活躍的租戶信息置於在不同的分區,這樣可以提高數據的訪問速度。
  • 開發者可以根據地點來對租戶進行分區。有種場景是某個特定的地理區域的租戶在該地區的非高峯時間離線備份和維護數據,而在其他地區的租戶的數據保持在線,可以正常訪問,這樣根據地點來進行分區更適用於該使用場景。
  • 開發者可以根據租戶的優先級的不同進行分區。將高值信息置於高效的,輕負載的分區,而一些低值數據置於那些一般的分區。
  • 如果某些租戶的信息需要高度的數據隔離和個人隱私保護的話,那麼開發者可以將這些高度敏感的信息置於單獨的分區。

擴展和數據移動操作

每種Sharding策略在管理向內擴展,向外擴展,數據移動,狀態保持等方面,都有着不同的能力級別。

查找策略允許擴展和數據移動這些操作操作在用戶級別進行,無論是在線或離線的進行。查找策略的技術就在於阻斷某些用戶的行爲(非高峯時段),將數據遷移到新的虛擬分區或者物理分區,修改映射,刷新任何持有數據的緩存,然後恢復之前阻斷的用戶行爲。通常來說,這類操作是可以集中進行管理。查找策略需要高度可緩存的並且友好複製分發的狀態。

範圍策略對擴展和數據遷移操作會帶來一定的侷限性,通常必須時進行數據存儲的一部分或全部下線,因爲數據分區必須要繼續分割,或者重新合併。如果最活躍的數據是相鄰Shard Key或數據的標識符在相同的範圍內,那麼通過遷移數據來重新平衡分區負載可能無法解決負載不均問題。範圍策略同樣需要維護一些狀態來將一定範圍的數據映射到物理分區。

哈希策略會令擴展和數據遷移的操作變得更加複雜,因爲分區的Key都是基於Shard Key的哈希值。所有新的分區都需要根據哈希值或者是實現映射的函數來計算判定。但是,哈希策略對於狀態的保持是沒有要求的。

問題和顧慮

在使用Sharding的時候,需要進行如下的考慮:

  • Sharding和垂直分區和功能分區是相互補充的。舉例來說,某個分片可能會包含垂直分區之後的實體,功能分區也可以在多個分片中進行。關於更多分區的信息,可以參考Data Partitioning Guidance
  • 儘量保證分區的負載是均衡的,這樣每個分區可以均分IO的負擔。隨着數據的插入和刪除,可能需要定期對分片進行重新平衡各個分區的數據,以減少產生熱點的可能。重新平衡數據是一個非常昂貴的操作。爲了減少重新平衡數據的操作,開發者應該儘量考慮每個分片的數據空間,保證每個分區的空間足夠。同時,開發者應該定製一些策略和腳本,能夠在需要的時候,對分區數據進行快速的重新平衡操作。
  • 注意使用穩定的數據來作爲Shard的Key。如果Shard的key修改了,那麼原來的數據也需要在分區之間進行移動,會爲更新操作帶來額外的工作。因此,不要讓Shard的Key包含那些潛在的可能修改的信息。最好使用一些無關的屬性,或者鍵值來構成Shard的Key。
  • 確保Shard的Key都是唯一的。例如,避免使用自增域作爲Shard的Key。在某些系統中,自增域可能無法在分區中調節,可能會不同分區中,擁有相同的Shard Key.

    Shard Key不包含自動增加的值字段也能產生問題。例如,如果你使用自動遞增字段生成唯一的ID,然後兩個數據項位於不同的分區可以被分配相同的ID。

  • 很多時候,想要設計一個合適的Shard Key來滿足所有的需求是不太可能的。只能將數據分區來支持最常執行的請求,如果有一些其他請求需求的話,可以創建第二個Index-Table來支持一些非Shard Key屬性的查詢。關於更多的信息,可以參考Index-Table模式

  • 針對單一的一個分區執行查詢獲取數據,要比從多個不同的分區獲取數據,再Join聚合來獲取數據的效率搞很多很多,所以,儘量避免跨越多個分區進行查詢操作。需要注意的是,一個分區可以包含多種類型的實體。考慮非規範化你的數據來保證相關的實體是在分片一起查詢的(如客戶的細節,他們下的訂單),以減少應用程序執行的請求數。

    如果一個分區中的實體引用了另一個分區中存儲的實體,那麼在設計第一個實體的結構的時候,最好將引用的實體的Shard Key作爲結構的一部分。這樣可以提高查詢引用實體的效率,增加跨分區查詢數據的性能。

  • 如果應用不得不請求多個分區的數據的話,那麼最好通過使用並行的任務來獲取數據。比如fan-out數據,數據以並行的方式從多個分區獲取,然後再聚合成一個結果。然而,這種方法不可避免的增加了數據訪問的邏輯的複雜度。

  • 對很多應用來說,創建大量的小分區數據要比有少量分區,但是大量數據的分區性能要好,因爲小分區更好的均衡工作負載。這種方法也在你需要的分區從一個物理位置遷移到另一個的時候也十分有效。遷移小的分區比遷移大的分區要快得多。
  • 確保給每個分區足夠的資源來處理擴展性需求所需要的數據規模和吞吐量。關於更多信息,可以參考Data Partitioning Guidance中的爲擴展性設計分區一節。
  • 考慮將分區數據所引用的數據到複製到分區中。如果一個請求從分區請求數據的同時還引用靜態或很少修改的數據,那麼將這些數據同樣添加到分區中。然後,應用程序可以輕鬆地獲取查詢的所有數據,而無需對單獨的數據存儲區進行額外查詢。

    如果引用的數據在其它分區修改了,那麼系統必須跨越分區同步這些變動。系統會在進行這些同步的期間產生一定的不一致。如果使用這種方法,開發者需要讓應用可以處理這種不一致。

  • 保證不同分區之間的數據一致是非常困難的,所以開發者應該儘量減少跨越多個分區的操作。如果應用必須跨越分區修改數據,那麼就需要評估數據一致性是否是實際的需求了。通常,在雲環境中一般是實現最終一致性來保證數據一致的。每個分區中的數據分別進行更新,由應用的邏輯來負責保證所有更新操作的成功,同時也負責處理更新數據過程中的請求時的數據不一致。想了解更多的關於最終一致性的信息,可以參考Data Consistency Primer

  • 配置和管理大量的分區也是一項挑戰。諸如監控,備份,檢查一致性,記錄日誌,驗證等任務必須在多個不同地區的分區和服務器上完成。這些任務任務通常通過自動化解決方案或者腳本來實現,但是腳本和自動化可能無法消除額外的管理需求。
  • 分區可以在地理位置上距離使用分片數據的應用服務器較近。這種方法可以降低網絡延時,提高性能,但是對於跨分區訪問的操作引入額外的複雜性。

何時使用該模式

  • 當數據倉庫需要擴展,但是單個數據節點的資源有些不足的時候可以考慮使用Sharding模式。
  • 當需要提高性能,減少數據倉庫中的爭用的時候,可以考慮使用Sharding模式。

Sharding模式主要的重點是在於提高性能和系統的可擴展性,而且它同時也因爲數據的獨立分區提高了可用性。一個分區中的故障並不會影響應用程序訪問在其他分區中保存的數據,並且運維人員可以對一個或多個分區執行維護或恢復,而不會令應用完全不可用。想了解更多相關信息,可以參考Data Partitioning Guidance

舉例

下面的例子使用一些SQL Server數據庫作爲分區。每個數據庫持有應用使用數據的一部分。應用從多個分區獲取數據。其中GetShards()方法返回所有數據所處於的分區。GetShards()方法返回了ShardInfomation對象的列表,其中ShardInfomation類包含了一個Shard的標識符,也包含了一個數據庫連接字符串由應用連接到對應的分區。

private IEnumerable<ShardInformation> GetShards()
{
    // This retrieves the connection information from a shard store
    // (commonly a root database).
    return new[]
    {
        new ShardInformation
        {
            Id = 1,
            ConnectionString = ...
        },
        new ShardInformation
        {
            Id = 2,
            ConnectionString = ...
        }
    };
}

下面的代碼展示了應用如何使用ShardInfomation對象列表來並行查詢數據的。其中具體請求的代碼沒有展示,但是在例子中,請求客戶的名字的信息,如果分區中包括客戶信息,就會返回。然後結果聚合到ConcurrentBag集合中,等待應用處理。

// Retrieve the shards as a ShardInformation[] instance.
var shards = GetShards();
var results = new ConcurrentBag<string>();
// Execute the query against each shard in the shard list.
// This list would typically be retrieved from configuration
// or from a root/master shard store.
Parallel.ForEach(shards, shard =>
{
    // NOTE: Transient fault handling is not included,
    // but should be incorporated when used in a real world application.
    using (var con = new SqlConnection(shard.ConnectionString))
    {
        con.Open();
        var cmd = new SqlCommand("SELECT ... FROM ...", con);
        Trace.TraceInformation("Executing command against shard: {0}", shard.Id);
        var reader = cmd.ExecuteReader();
        // Read the results in to a thread-safe data structure.
        while (reader.Read())
        {
            results.Add(reader.GetString(0));
        }
    }
});
Trace.TraceInformation("Fanout query complete - Record Count: {0}",
results.Count);

相關的其他模式

在考慮使用Sharding模式的時候,可以參考以下文章:

  • Data Consistency Primer.有些時候,可能需要維護分佈在不同的分區數據的一致性。Data Consistency Primer總結了圍繞分佈式數據保持一致性的問題,並介紹了不同的一致性模型的好處和使用代價。
  • Data Partitioning Guidance.將數據存儲進行分區可能引入一些列的問題,Data Partitioning Guidance中針對雲環境數據分區在增加擴展性,降低爭用以及優化性能等問題上進行了描述。
  • Index-Table模式.有些時候,僅僅通過設計Shard的Key是無法滿足所有的查詢請求的。Index-Table模式令應用能夠從大量數據中通過非Shard Key來進行快速檢索。
  • Materialized-View模式.爲了保證一些查詢操作的性能,最好創建一些具體化視圖來聚合數據,尤其這些數據是跨越跨越多個分區的時候。Materialized-View模式描述瞭如何生成和構成這些視圖。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章