1.背景
現在,我們有這樣一個場景:我們的媒體上傳服務每天都會接收海量的數據,面對這麼多的海量數據,單台服務器已經不足以存儲得下了,我們需要考慮水平擴容多台機器來聯合存儲這些海量數據。但是每台服務器又都是相互獨立的,而且數據也不能亂存,萬一發生存儲傾斜,導致有的服務器硬盤馬上爆炸🎆,而有的服務器則似閒庭信步😀😨,毫無存儲壓力,這怎麼能行。
因此,我們就採用傳統的Hash存儲方式來解決這個問題:假如我們目前有3台服務器,那麼當用户上傳來一份數據後,我們就對其文件內容進行Hash(舉個例子而已,實際這麼做,上班需要帶着頭盔才能去😋),然後對Hash值再模3(服務器的台數),最終根據計算得到的offset來選擇某一台服務器進行存儲即可。
乍一聽上去,這個方案彷彿解了我們的燃眉之急,但是它存在一個隱患——進行服務器水平擴縮容時,可能會大概率面臨幾乎全部數據都要rehash + relocate一遍的糟糕情況。
那我們該如何解決呢?哎,這下我們就得拿出進階版的Hash解決方案了:一致性Hash!
2.實現方式
執行流程:
- 先根據服務器的IP或者地理位置進行
Hash運算,然後對232取模,得到服務器節點在Hash環上的位置。 - 然後當前有新的數據到達時,同樣對其進行
Hash運算,然後再對232取模,然後得到其在Hash環上的位置。然後順時針尋找離它捱得最近的服務器節點進行存儲。
進行服務器水平擴縮容後,這裏具體為增加1台服務器。
執行流程:
- 你會發現,我們僅需要遷移服務器1和服務器4這兩台服務器對應的
Hash點之間的數據點即可(因為是順時針選擇存儲服務器節點),而其他的數據點動都不用動。這就是一致性Hash算法的優勢所在——降低了水平擴縮容需要大規模移動數據的風險。
:::info
Q:為什麼選擇232而不是其他數值?
A:
- 起初,一致性Hash算法是用在32位系統上的,使用這個數值意味着可以利用整個尋址空間。
- 很多常見的Hash函數,比如
CRC32等,最終都會產生32位的Hash值,這樣就無需二次處理得到的Hash值了。 232已經可以提供足夠大的空間來避免Hash衝突了,因此再加容量來進一步避免Hash衝突性價比不高。232已經提供了足夠高的粒度供數據節點均勻分佈在Hash環上了。- 為了兼容不同的分佈式系統和存儲系統,減少不必要的集成和遷移成本。
:::
3.存在的問題
數據偏斜問題
直觀上來看是Hash節點分佈不均,因此具體情況分為:
-
服務器Hash節點分佈不均。
- 在這種情況下(就上圖而言),node-4自己要承載接近總流量的一半的壓力,這並沒有達到負載均衡的目標。
- 一旦node-4宕機,那麼這一半的數據節點都需要進行遷移,開銷也挺大的,而且隨後,node-2所存儲的數據將會猛增,同時所要承載的流量也將會猛增,這個時候node-2也有可能會因此而宕機,於是乎雪崩式的的連鎖反應出現了。。。顯然,數據偏斜問題會給整個服務系統帶來不穩定不確定的影響。
- 數據Hash節點分佈不均。
解決方案
將一個實際的服務節點用多個虛擬的服務節點(vnodes)來代替,然後將虛擬的服務節點映射到Hash環上以通過(偽)增加服務節點的方式來解決節點分佈不均的問題。
你會發現,目前的分佈狀態下,流量到達時,都可以被均攤到每個服務節點上,同時,當某個服務節點宕機後,它的遺留下來的數據和所要承載的流量均能被其他節點所分攤而不是隻有一個節點來承擔,這提高了服務系統的整體的穩定性。
同時,我們也可以根據服務器的配置高低,相對應的多分配或者少分配一些虛擬服務節點,從而變相實現了多服務器下的負載均衡的權重特性。
4.優點
避免了在服務器水平擴縮容的情況下可能出現的數據大規模遷移的糟糕情況,使服務擴縮容變得更加容易便捷。
參考文檔
9.4 什麼是一致性哈希?