1. 程式人生 > >數據分區------《Designing Data-Intensive Applications》讀書筆記9

數據分區------《Designing Data-Intensive Applications》讀書筆記9

zookeep 組件 搜索 介紹 程序 cas 只有一個 核心技術 熱點

進入到第六章了,我們要開始聊聊分布式系統之中的核心問題:數據分區。分布式系統通常是通過大規模的數據節點來處理單機沒有辦法處理的海量數據集,因此,可以將一個大型數據集可以分布在多個磁盤上,查詢負載可以分布在多個處理器上。在這一章中,我們首先討論劃分大型數據集的不同方法,並觀察數據索引如何與分區交互,然後將探索數據分區重新平衡的策略。最後,來看看路由技術怎麽將查詢索引到正確的分區。內容看起來還不少,我們開始吧。

1. 分區與副本

分區與副本是很容易混淆的概念,我們這裏離清一下兩者。
數據分區的每個副本可以存儲在多個節點上。這意味著,即使每個記錄恰好屬於一個分區,它仍然可以存儲在幾個不同的節點上進行容錯。
技術分享圖片

由上圖可以看出,分區和副本是需要解決不同問題的,並不能混為一談,兩者同樣作為分布式系統之中的核心技術,共同為分布式系統提供好的解決方案。

2. 分區策略

數據分區的目的是:將數據和查詢負載均勻地分布在節點上。其實副本也有同樣的效果,取決於副本同步機制)而如果數據分區不公平,則會出現某些分區的數據或查詢比其他分區要多,我們稱之為偏斜。數據偏斜就使得分區效果變差,導致負載不均衡形成分區熱點。 所以分區策略通常以分區均勻為考量,接下來我們介紹幾種常見的分區策略:

範圍分區

範圍分區是分配一個連續的範圍鍵,如同幾冊百科全書一般。如果知道範圍之間的邊界,就可以很容易地確定哪個分區包含給定的鍵。如果您還知道哪個分區被分配到哪個節點,那麽您可以直接將請求發送到適當的節點。

技術分享圖片

但是很多時候鍵的範圍不一定是均勻分布的,為了均勻地分布數據,分區邊界需要和數據的特點相適應。對於每一個分區,我們可以把按鍵順序排列,如SSTable,顯然這樣可以大幅提升範圍掃描的效率。

而範圍分區的缺點是某些訪問模式會導致熱點。如果某個範圍的鍵頻繁被訪問,將導致某個分區的讀寫量遙遙領先,而其他分區被閑置。(這種情況考慮細分分區粒度或者級聯索引,用一個較均勻的特征先做一次分區

哈希分區

由於範圍分區容易產生熱點問題,許多分布式數據存儲使用一個哈希函數來確定一個鍵值的分區。一個好的哈希函數可以將傾斜的數據均勻分布,即使數據範圍很接近,但是它們的哈希值是均勻分布的值。如下圖所示,時間接近的鍵值被哈希函數均勻的分區在多個分區,每個鍵的哈希值落在一個分區的範圍將被存儲在該分區:
技術分享圖片

使用哈希分區,我們失去了鍵範圍分區的一個很好的特性,曾經相鄰的鍵現在分散在所有分區上,因此它們的排序順序丟失。我們可以通過級聯索引的方式解決這個問題。級聯索引方法支持一對多關系的優雅的數據模型,通過兩分區方式來綜合不同分區方式的優點,通過鍵哈希來確定分區的第一部分,但其他列作為SSTables的數據排序串聯。因此,查詢不能在復合鍵的第一列內搜索範圍內的值,但是如果它為第一列指定一個固定值,它就可以在鍵的其他列上執行有效的範圍掃描。例如,在社交媒體站點上,一個用戶可以發布許多更新。如果更新主鍵(user_id,update_timestamp),那麽你可以有效地檢索在一定時間間隔內特定用戶的所有更新。不同的用戶可以存儲在不同的分區上,但是在每個用戶中,更新是在單個分區上以時間戳順序存儲的。

Tip:緩解熱點

通過哈希函數分區的確有助於減少熱點。然而,它不能完全避免它們:在極端情況下,所有讀寫操作都是相同的鍵,最終仍然會將所有請求到同一分區。例如,在社交媒體網站上,一個擁有數百萬追隨者的名人用戶在做某事時可能會引發一場讀寫風暴。此事件可能導致短時間內大量寫入同一個鍵(其中的Key可能是名人的用戶ID,或者是人們評論的行為ID)。這時哈希函數也無能為力,因為兩個相同ID的哈希值仍然相同。

大多數數據系統不能自動補償這種高度傾斜的工作負載,因此應用程序有責任減少偏斜。例如,如果已知一個鍵非常熱,一個簡單的方法就是在鍵的開頭或結尾加上一個隨機數。只有一個兩位數的十進制隨機數將把寫入分成100個不同的鍵,允許這些鍵被分配到不同的分區。但是將不同的鍵分開寫入後,現在任何讀取都必須做額外的工作,因為它們必須從所有100個鍵讀取數據並將其組合起來。而且這種方式還需要額外的記錄:因為只為少量的熱鍵添加隨機數是有意義的;對於絕大多數具有低寫入吞吐量的鍵,這將是不必要的開銷。因此,還需要一些方法來跟蹤哪些鍵正在被分割。

2. 分區與二級索引

上文討論的分區方案依賴於一個關鍵值數據模型。通過主鍵訪問記錄,可以由該鍵確定分區,並使用它將讀取和寫入請求路由到負責該鍵的分區。

而一旦涉及到二級索引,情況會變得更加復雜。二級索引通常不確定記錄的唯一性而應該是尋找一個特定的值出現的方式如:找到所有顏色是紅色的車 這樣的查詢。二級索引的問題是它不能映射到分區。有兩種主要方法將數據庫分為二級索引:基於分區的索引和基於全局的索引。

基於分區的索引

假如有一個賣二手車的網站,每個列表都有一個唯一的ID,稱之為文檔。通過文檔id(例如,分區0中的IDS 0到499、分區1中的IDS 500到999)對數據庫進行分區。
您希望讓用戶搜索汽車,允許它們按顏色和按顏色進行過濾,因此需要對顏色進行二級索引索引,每當一輛紅色的車是添加到數據庫中,數據庫分區自動添加到索引的文檔的ID到紅色索引處。如下圖所示:
技術分享圖片

在這種索引方法中,每個分區都是完全獨立的,每個分區都保留自己的索引,只覆蓋分區中的文檔id。它不關心存儲在其他分區中的數據。每當您需要向數據庫寫入添加、刪除或更新文檔時,只需要處理包含您正在編寫的文檔ID的分區。

但是,從索引讀取時需要註意,如果您想搜索紅色的汽車,您需要將查詢發送到所有分區,並將所有返回的結果組合起來。這樣導致了二級索引上的讀取查詢非常耗時。即使並行的寫入和查詢分區,分散/聚集操作會導致延遲放大。

基於全局的索引

上節提到分區索引的缺點,所以我們可以建立一個全局的索引,涵蓋所有的分區數據。但是,不能只存儲索引在一個節點,因為它可能會成為一個瓶頸和故障點。所以全局索引也必須被分區,但它可以劃分不同的主鍵索引。如下圖所示:
技術分享圖片
全局索引使讀操作更加高效:而不用分散/聚集所有分區的數據。但全球索引的缺點是,寫入速度較慢,更復雜,因為寫一個文件現在可以影響指數的多個分區。(文件中的每一項可能會在不同的分區,在不同的節點上,在實踐之中,二級全局索引通常通過異步的方式進行更新)。

3 分區平衡

隨著時間的推移,數據庫中的東西發生了變化:

  • (1) 查詢吞吐量增加,因此您需要添加更多CPU來處理負載。
  • (2) 數據集大小增加,所以您需要添加更多的磁盤和RAM來存儲它。
  • (3) 機器故障,其他機器需要接管故障機器的責任。

所有這些變化都要求將數據和請求從一個節點移動到另一個節點。而負載從集群中的一個節點轉移到另一個節點的過程稱為分區平衡

無論采用哪種分區方案,通常都希望分區平衡以滿足下面的要求:

  • (1) 重新平衡後,集群中的節點之間應該公平地共享負載(數據存儲、讀寫請求)。
  • (2) 當分區平衡工作時,數據庫應該繼續接受讀寫操作。
  • (3) 在節點之間移動盡量減少數據的移動,以便使平衡快速完整,並減少網絡和磁盤I/O負載。

海量分區

節點創建遠超節點數目的分區數,並為每個節點分配幾個分區。例如,在10個節點的群集上運行的數據庫可以從一開始分裂成1000個分區,以便分配給每個節點大約100個分區。當將一個節點添加到集群中,新節點可以從每個現有節點竊取一些分區,直到再次公平分配分區為止。如下圖所示:
技術分享圖片

分區的數量不會改變,分區的鍵分配也不會改變。唯一改變的是分區與節點之間的映射。這種分區平衡的變化不是即時的,在網絡上傳輸大量數據需要一定的時間,所以舊的分區節點在分區平衡時需要承擔這個過程之中的讀寫操作。通過海量分區同樣也可以通過給性能更強悍的節點分配更多的分區,可以強制這些節點承擔更大的負載份額。

一開始配置的分區數量就是所能擁有的最大節點數,因此您需要選擇足夠高的分區數目以適應未來的增長。然而,每個分區也有管理開銷,所以選擇過高的值會適得其反。

動態分區

對於使用鍵範圍分區的數據庫,固定範圍值的固定分區數量將非常不方便:如果您的邊界錯誤,您可能會將所有數據放在一個分區中,而所有其他分區都是空的。手動重新分區分區將非常繁瑣。所以可以采取動態分區的機制:

當一個分區的增長超過配置的大小,它被分為兩個分區,大約一半的數據分配在兩個新的分區。相反,如果大量數據被刪除,一個分區縮小到某個閾值以下,它可以與相鄰分區合並。動態分區的優點是分區的數量與總數據量相適應。如果只有少量的數據,少量的分區就足夠了,因此開銷很小;如果有大量的數據,每個單獨的分區的大小限制為一個可配置的最大值。

4. 請求路由

在多臺機器上運行的多個節點上對數據集進行分區,所以會面臨一個核心問題:當客戶端想要提出請求時,它如何知道要連接哪個節點?當分區被重新平衡,分區節點變化的時候客戶端如何感知變化。

在高層次上,對這個問題有幾種不同的解決方案:

  • 1.允許客戶端與任何節點聯系。如果該節點恰好擁有請求所應用的分區,則它可以直接處理請求;否則,它將請求轉發到適當的節點,接收應答,並將應答傳遞給客戶端。
    1. 將客戶端的所有請求首先發送到路由層,這將決定應處理每個請求並相應轉發它的節點。
    1. 要求客戶端知道分區和分配給節點的分區。在這種情況下,客戶機可以直接連接到適當的節點,而不需要任何中介。

技術分享圖片

在三種情況之中關鍵的問題是:組成路由決策的組件(可能是其中一個節點,或者路由層,或客戶端)如何了解分區分配給節點的變化?

許多分布式數據系統依賴於一個單獨的協調服務如ZooKeeper跟蹤這個集群的元數據,每個節點在ZooKeeper之中註冊自己。ZooKeeper維護分區節點映射的權威,而路由層或客戶端,可以訂閱這個ZooKeeper。當一個分區發生變化時,或添加一個節點或刪除,ZooKeeper通知路由層,這樣可以保持它的路由信息更新。如下圖所示:
技術分享圖片

Cassandra和Riak采取了不同的方法:通過使用Gossip協議節點之間傳播集群狀態的任何變化。請求可以發送到任何節點,該節點將它們轉發到所請求分區的適當節點。該模型提出了更復雜的數據庫節點,但避免了外部協調服務的依賴。

當使用路由層或向隨機節點發送請求時,客戶端仍然需要找到連接到的IP地址。這些並不像分區對節點的分配那樣快速變化,因此經常使用DNS來達到此目的。

小結:

我們在本篇之中總結了數據分區技術運用到的多種策略與技術,希望大家能夠更好的認識數據分區技術在分布式存儲之中的重要意義。

數據分區------《Designing Data-Intensive Applications》讀書筆記9