一致性Hash
一致性Hash演算法背景
一致性哈希演算法在1997年由麻省理工學院的Karger等人在解決分散式Cache中提出的,設計目標是為了解決網際網路中的熱點(Hot spot)問題,初衷和CARP十分類似。一致性哈希修正了CARP使用的簡單哈希演算法帶來的問題,使得DHT可以在P2P環境中真正得到應用。
但現在一致性hash演算法在分散式系統中也得到了廣泛應用,研究過memcached緩存資料庫的人都知道,memcached伺服器端本身不提供分散式cache的一致性,而是由客戶端來提供,具體在計算一致性hash時採用如下步驟:
- 首先求出memcached伺服器(節點)的哈希值,並將其配置到0~232的圓(continuum)上。
- 然後採用同樣的方法求出存儲數據的鍵的哈希值,並映射到相同的圓上。
- 然後從數據映射到的位置開始順時針查找,將數據保存到找到的第一個伺服器上。如果超過232仍然找不到伺服器,就會保存到第一台memcached伺服器上。
從上圖的狀態中添加一台memcached伺服器。餘數分散式演算法由於保存鍵的伺服器會發生巨大變化而影響緩存的命中率,但Consistent Hashing中,只有在園(continuum)上增加伺服器的地點逆時針方向的第一台伺服器上的鍵會受到影響,如下圖所示:
一致性Hash性質
考慮到分散式系統每個節點都有可能失效,並且新的節點很可能動態的增加進來,如何保證當系統的節點數目發生變化時仍然能夠對外提供良好的服務,這是值得考慮的,尤其實在設計分散式緩存系統時,如果某台伺服器失效,對於整個系統來說如果不採用合適的演算法來保證一致性,那麼緩存於系統中的所有數據都可能會失效(即由於系統節點數目變少,客戶端在請求某一對象時需要重新計算其hash值(通常與系統中的節點數目有關),由於hash值已經改變,所以很可能找不到保存該對象的伺服器節點),因此一致性hash就顯得至關重要,良好的分散式cahce系統中的一致性hash演算法應該滿足以下幾個方面:
- 平衡性(Balance)
平衡性是指哈希的結果能夠儘可能分布到所有的緩衝中去,這樣可以使得所有的緩衝空間都得到利用。很多哈希演算法都能夠滿足這一條件。
- 單調性(Monotonicity)
單調性是指如果已經有一些內容通過哈希分派到了相應的緩衝中,又有新的緩衝區加入到系統中,那麼哈希的結果應能夠保證原有已分配的內容可以被映射到新的緩衝區中去,而不會被映射到舊的緩衝集合中的其他緩衝區。簡單的哈希演算法往往不能滿足單調性的要求,如最簡單的線性哈希:x = (ax + b) mod (P),在上式中,P表示全部緩衝的大小。不難看出,當緩衝大小發生變化時(從P1到P2),原來所有的哈希結果均會發生變化,從而不滿足單調性的要求。哈希結果的變化意味著當緩衝空間發生變化時,所有的映射關係需要在系統內全部更新。而在P2P系統內,緩衝的變化等價於Peer加入或退出系統,這一情況在P2P系統中會頻繁發生,因此會帶來極大計算和傳輸負荷。單調性就是要求哈希演算法能夠應對這種情況。
- 分散性(Spread)
在分散式環境中,終端有可能看不到所有的緩衝,而是只能看到其中的一部分。當終端希望通過哈希過程將內容映射到緩衝上時,由於不同終端所見的緩衝範圍有可能不同,從而導致哈希的結果不一致,最終的結果是相同的內容被不同的終端映射到不同的緩衝區中。這種情況顯然是應該避免的,因為它導致相同內容被存儲到不同緩衝中去,降低了系統存儲的效率。分散性的定義就是上述情況發生的嚴重程度。好的哈希演算法應能夠盡量避免不一致的情況發生,也就是盡量降低分散性。
- 負載(Load)
負載問題實際上是從另一個角度看待分散性問題。既然不同的終端可能將相同的內容映射到不同的緩衝區中,那麼對於一個特定的緩衝區而言,也可能被不同的用戶映射為不同的內容。與分散性一樣,這種情況也是應當避免的,因此好的哈希演算法應能夠盡量降低緩衝的負荷。
- 平滑性(Smoothness)
平滑性是指緩存伺服器的數目平滑改變和緩存對象的平滑改變是一致的。
原理
基本概念
一致性哈希演算法(Consistent Hashing)最早在論文《Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web》中被提出。簡單來說,一致性哈希將整個哈希值空間組織成一個虛擬的圓環,如假設某哈希函數H的值空間為0-2^32-1(即哈希值是一個32位無符號整形),整個哈希空間環如下:
整個空間按順時針方向組織。0和232-1在零點中方向重合。
下一步將各個伺服器使用Hash進行一個哈希,具體可以選擇伺服器的ip或主機名作為關鍵字進行哈希,這樣每台機器就能確定其在哈希環上的位置,這裡假設將上文中四台伺服器使用ip地址哈希後在環空間的位置如下:
接下來使用如下演算法定位數據訪問到相應伺服器:將數據key使用相同的函數Hash計算出哈希值,並確定此數據在環上的位置,從此位置沿環順時針「行走」,第一台遇到的伺服器就是其應該定位到的伺服器。
例如我們有Object A、Object B、Object C、Object D四個數據對象,經過哈希計算後,在環空間上的位置如下:
根據一致性哈希演算法,數據A會被定為到Node A上,B被定為到Node B上,C被定為到Node C上,D被定為到Node D上。
下面分析一致性哈希演算法的容錯性和可擴展性。現假設Node C不幸宕機,可以看到此時對象A、B、D不會受到影響,只有C對象被重定位到Node D。一般的,在一致性哈希演算法中,如果一台伺服器不可用,則受影響的數據僅僅是此伺服器到其環空間中前一台伺服器(即沿著逆時針方向行走遇到的第一台伺服器)之間數據,其它不會受到影響。
下面考慮另外一種情況,如果在系統中增加一台伺服器Node X,如下圖所示:
此時對象Object A、B、D不受影響,只有對象C需要重定位到新的Node X 。一般的,在一致性哈希演算法中,如果增加一台伺服器,則受影響的數據僅僅是新伺服器到其環空間中前一台伺服器(即沿著逆時針方向行走遇到的第一台伺服器)之間數據,其它數據也不會受到影響。
綜上所述,一致性哈希演算法對於節點的增減都只需重定位環空間中的一小部分數據,具有較好的容錯性和可擴展性。
另外,一致性哈希演算法在服務節點太少時,容易因為節點分部不均勻而造成數據傾斜問題。例如系統中只有兩台伺服器,其環分布如下,
此時必然造成大量數據集中到Node A上,而只有極少量會定位到Node B上。為了解決這種數據傾斜問題,一致性哈希演算法引入了虛擬節點機制,即對每一個服務節點計算多個哈希,每個計算結果位置都放置一個此服務節點,稱為虛擬節點。具體做法可以在伺服器ip或主機名的後面增加編號來實現。例如上面的情況,可以為每台伺服器計算三個虛擬節點,於是可以分別計算 「Node A#1」、「Node A#2」、「Node A#3」、「Node B#1」、「Node B#2」、「Node B#3」的哈希值,於是形成六個虛擬節點:
同時數據定位演算法不變,只是多了一步虛擬節點到實際節點的映射,例如定位到「Node A#1」、「Node A#2」、「Node A#3」三個虛擬節點的數據均定位到Node A上。這樣就解決了服務節點少時數據傾斜的問題。在實際應用中,通常將虛擬節點數設置為32甚至更大,因此即使很少的服務節點也能做到相對均勻的數據分布。
推薦閱讀:
※精選 TOP45 值得學習的Python項目
※基數排序
※【轉藏】民間八字最准演算法無與論比
※動態規劃求解最長不重疊子串
※『知日元斷流年』的推演算法則(司瑩居士)