1. 程式人生 > >分布式緩存一致性哈希算法

分布式緩存一致性哈希算法

解壓 緩解壓力 分享圖片 bubuko 保護 成了 繼續 gpo 結果

參考:http://www.cnblogs.com/mikevictor07/p/4458736.html

一、簡介

關於一致性哈希算法介紹有許多類似文章,需要把一些理論轉為為自己的知識,所以有了這篇文章,本文部分實現也參照了原有的一些方法。該算法在分布緩存的主機選擇中很常用,詳見http://en.wikipedia.org/wiki/Consistent_hashing 。

二、算法誕生緣由

現在許多大型系統都離不開緩存(K/V)(由於高並發等因素照成的數據庫壓力(或磁盤IO等)超負荷,需要緩存緩解壓力),為了獲得良好的水平擴展性,緩存主機互相不通信(如Mencached),通過客戶端計算Key而得到數據存放的主機節點,最簡單的方式是取模,假如:

----------------------------------------------------------------

現在有3臺緩存主機,現在有一個key 為 cks 的數據需要存儲:

key = "cks"

hash(key) = 10

10 % 3 = 1 ---> 則代表選擇第一臺主機存儲這個key和對應的value。

缺陷:

假如有一臺主機宕機或增加一臺主機(必須考慮的情況),取模的算法將導致大量的緩存失效(計算到其他沒有緩存該數據的主機),數據庫等突然承受巨大負荷,很大可能導致DB服務不可用等。

----------------------------------------------------------------

三、一致性哈希算法原理

該算法需要解決取模方法當增加主機或者宕機時帶來的大量緩存抖動問題,要在生產環境中使用,算法需具備以下幾個特點:

1. 平衡性 : 指緩存數據盡量平衡分布到所有緩存主機上,有效利用每臺主機的空間。

2.

3. 負載均衡 : 每臺緩存主機盡量平衡分擔壓力,即Key的分配比例在這些主機中應趨於平衡。

4.分散性:在分布式環境中,終端有可能看不到所有的緩沖,而是只能看到其中的一部分。當終端希望通過哈希過程將內容映射到緩沖上時,由於不同終端所見的緩沖範圍有可能不同,從而導致哈希的結果不一致,最終的結果是相同的內容被不同的終端映射到不同的緩沖區中。這種情況顯然是應該避免的,因為它導致相同內容被存儲到不同緩沖中去,降低了系統存儲的效率。分散性的定義就是上述情況發生的嚴重程度。好的hash算法應能夠盡量避免不一致的情況發生,也就是盡量降低分散性。

使用場景

現在我們假設有100臺redis data服務器,一份數據101進來的時候,以散列公式hash(i)&100,計算所存放的服務器,假設hash(i) = i,那麽數據被散列到標號為1的服務器,然後這個時候服務器新增了一臺,然後散列公式為hash(i)%101,這個時候請求訪問數據101的時候,被分配至0號服務器,但是其實這個時候數據是在1號服務器的。

所以這個時候大量的數據失效了(訪問不到了)。

所以這個時候,我們假設是新增了服務器,如果是持久化存儲的,我們可以讓服務器集群對數據進行重新散列,進行數據遷移,然後進行恢復,但是這個時候就意味著每次增減服務器的時候,集群就需要大量的通信,進行數據遷移,這個開銷是非常大的。如果只是緩存,那麽緩存就都失效了。所以這個時候怎麽辦?

我們可以看到,關鍵問題在於,服務器數量變動的時候,要能夠保證舊的數據能夠按照老的算法,計算到數據所在的服務器,而新的數據能夠按照新的散列算法,計算出數據所在的服務器。

技術分享圖片

如上圖,我們有ABCD四臺服務器,這四臺服務器被分配至0~232 的一個環上,比如0~230的存儲在A服務器,230 +1~231 存儲到B服務器上.....CD按照這樣的進行均分。將我們的散列空間也劃為0~232 ,然後數據進來後對232 取模,得到一個值K1,我們根據K1在環上所處的位置,得到所分配到的服務器,如圖,K1被分配到B服務器。 這個時候,我們有一臺服務器B失效了。

技術分享圖片

我們可以看到,如果是B失效了,那麽如果有持久化存儲的,需要做數據恢復,將B的數據遷移至C即可,對於原本散列在A和D的數據,不需要做任何改變。 同理,如果我們是新增了服務器,那麽只需要對一臺服務器的數據遷移一部分至新加的服務器即可。

一致性hash算法,減少了數據映射關系的變動,不會像hash(i)%N那樣帶來全局的變動

而且這樣還有個好處,假設我們使用UID作為散列範圍(即上面的232 ),那麽假設有部分UID的訪問很頻繁,而且這部分UID集中在B服務器上,那麽就造成了B的負載遠遠高於其他服務器。這就是熱點數據的問題。這個時候我們可以向B所在的UID空間添加服務器,減少B的壓力。

其實還有個更好的解決辦法:虛擬節點。

上面說的情況是,使用真實的服務器作為節點散列在232 上。 我們假設,只有4臺服務器(如上圖),然後A上面有熱點數據,結果A掛掉了,然後做數據恢復,A的數據遷移至B,然後B需要承受A+B的數據,也承受不住,也掛了。。。。然後繼續CD都掛了。這就造成了

雪崩效應。

上面會造成雪崩效應的原因分析:
如果不存在熱點數據的時候,每臺機器的承受的壓力是M/2(假設每臺機器的最高負載能力為M),原本是不會有問題的,但是,這個時候A服務器由於有熱點數據掛了,然後A的數據遷移至B,導致B所需要承受的壓力變為M(還不考慮熱點數據訪問的壓力),所以這個失敗B是必掛的,然後C至少需要承受1.5M的壓力。。。。然後大家一起掛。。。
所以我們通過上面可以看到,之所以會大家一起掛,原因在於如果一臺機器掛了,那麽它的壓力全部被分配到一臺機器上,導致雪崩。

如果我們A掛了以後,數據被平均分配到BCD上,每臺機器多承受M/6的壓力,然後大家就都不會掛啦(不考慮熱點數據)。

這裏引入虛擬節點,如圖:

技術分享圖片

環上的空間被劃分為8份,然後A存儲A1和A2。。。
這個時候,如果A服務器掛了,訪問壓力會分配至C2和D1,也就是C和D服務器,而不是像前面,全部被分配到B上。

引入虛擬節點,主要在於,如果一臺服務器掛了,能夠將壓力引流至不同的服務器。

總結:一致性hash算法(DHT)通過減少影響範圍的方式解決了增減服務器導致的數據散列問題,從而解決了分布式環境下負載均衡問題,如果存在熱點數據,那麽通過增添節點的方式,對熱點區間進行劃分,將壓力分配至其他服務器。重新達到負載均衡的狀態。

分布式緩存一致性哈希算法