帶你詳細瞭解,一致性哈希算法的實現原理

背景

一致性哈希算法在 1997 年由麻省理工學院的 Karger 等人在解決分佈式 Cache 中提出的,設計目標是爲了解決因特網中的熱點問題,初衷和 CARP 十分類似。一致性哈希修正了 CARP 使用的簡單哈希算法帶來的問題,使得 DHT 可以在 P2P 環境中真正得到應用。

但現在一致性哈希算法在分佈式系統中也得到了廣泛應用,研究過 Memcached 緩存數據庫的人都知道,Memcached 服務器端本身不提供分佈式 Cache 的一致性,而是由客戶端來提供,具體在計算一致性哈希時採用如下步驟:

  1. 首先求出 Memcached 服務器(節點)的哈希值,並將其配置到0~2^32的圓(continuum)上。
  2. 然後採用同樣的方法求出存儲數據的鍵的哈希值,並映射到相同的圓上。
  3. 然後從數據映射到的位置開始順時針查找,將數據保存到找到的第一個服務器上。如果超過2^32仍然找不到服務器,就會保存到第一臺 Memcached 服務器上。

consistent-hash-1

從上圖的狀態中添加一臺 Memcached 服務器。餘數分佈式算法由於保存鍵的服務器會發生巨大變化而影響緩存的命中率,但在一致性哈希中,只有在圓(continuum)上增加服務器的地點逆時針方向的第一臺服務器上的鍵會受到影響,如下圖所示:

consistent-hash-2

性質

考慮到分佈式系統每個節點都有可能失效,並且新的節點很可能動態的增加進來,如何保證當系統的節點數目發生變化時仍然能夠對外提供良好的服務,這是值得考慮的,尤其實在設計分佈式緩存系統時,如果某臺服務器失效,對於整個系統來說,如果不採用合適的算法來保證一致性,那麼緩存在系統中的所有數據都可能會失效(即由於系統節點數目變少,客戶端在請求某一對象時需要重新計算其hash值,通常與系統中的節點數目有關,由於hash值已經改變,所以很可能找不到保存該對象的服務器節點),因此一致性哈希就顯得至關重要,良好的分佈式 Cahce 系統中的一致性哈希算法應該滿足以下幾個方面。

平衡性(Balance)

平衡性是指哈希的結果能夠儘可能分佈到所有的緩衝中去,這樣可以使得所有的緩衝空間都得到利用。很多哈希算法都能夠滿足這一條件。

單調性(Monotonicity)

單調性是指如果已經有一些內容通過哈希分派到了相應的緩衝中,又有新的緩衝區加入到系統中,那麼哈希的結果應能夠保證原有已分配的內容可以被映射到新的緩衝區中去,而不會被映射到舊的緩衝集合中的其他緩衝區。簡單的哈希算法往往不能滿足單調性的要求,如最簡單的線性哈希:x = (ax + b) mod (P),在上式中,P表示全部緩衝的大小。不難看出,當緩衝大小發生變化時(從P1P2),原來所有的哈希結果均會發生變化,從而不滿足單調性的要求。哈希結果的變化意味着當緩衝空間發生變化時,所有的映射關係需要在系統內全部更新。而在 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 位無符號整形),整個哈希空間環如下:

consistent-hash-3

整個空間按順時針方向組織,02^32-1在零點中方向重合。

下一步將各個服務器使用H進行一個哈希,具體可以選擇服務器的 IP 或主機名作爲關鍵字進行哈希,這樣每臺機器就能確定其在哈希環上的位置,這裏假設將上文中四臺服務器使用 IP 地址哈希後在環空間的位置如下:

consistent-hash-4

接下來,使用如下算法定位數據訪問到相應服務器:將數據key使用相同的函數H計算出哈希值,並確定此數據在環上的位置,從此位置沿環順時針“行走”,第一臺遇到的服務器就是其應該定位到的服務器。例如我們有Object AObject BObject CObject D四個數據對象,經過哈希計算後,在環空間上的位置如下:

consistent-hash-5

根據一致性哈希算法,數據A會被定爲到Node A上,B被定爲到Node B上,C被定爲到Node C上,D被定爲到Node D上。

下面分析一致性哈希算法的容錯性和可擴展性。現假設Node C不幸宕機,可以看到此時對象ABD不會受到影響,只有C對象被重定位到Node D。一般的,在一致性哈希算法中,如果一臺服務器不可用,則受影響的數據僅僅是此服務器到其環空間中前一臺服務器(即沿着逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。

下面考慮另外一種情況,如果在系統中增加一臺服務器Node X,如下圖所示:

consistent-hash-6

此時對象Object ABD不受影響,只有對象C需要重定位到新的Node X 。一般的,在一致性哈希算法中,如果增加一臺服務器,則受影響的數據僅僅是新服務器到其環空間中前一臺服務器(即沿着逆時針方向行走遇到的第一臺服務器)之間數據,其它數據也不會受到影響。

綜上所述,一致性哈希算法對於節點的增減都只需重定位環空間中的一小部分數據,具有較好的容錯性和可擴展性。

另外,一致性哈希算法在服務節點太少時,容易因爲節點分部不均勻而造成數據傾斜問題。例如系統中只有兩臺服務器,其環分佈如下,

consistent-hash-7

此時必然造成大量數據集中到Node A上,而只有極少量會定位到Node B上。爲了解決這種數據傾斜問題,一致性哈希算法引入了虛擬節點機制,即對每一個服務節點計算多個哈希,每個計算結果位置都放置一個此服務節點,稱爲虛擬節點。具體做法可以在服務器 IP 或主機名的後面增加編號來實現。例如上面的情況,可以爲每臺服務器計算三個虛擬節點,於是可以分別計算Node A#1Node A#2Node A#3Node B#1Node B#2Node B#3的哈希值,於是形成六個虛擬節點:

consistent-hash-8

同時數據定位算法不變,只是多了一步虛擬節點到實際節點的映射,例如定位到Node A#1Node A#2Node A#3三個虛擬節點的數據均定位到Node A上,這樣就解決了服務節點少時數據傾斜的問題。

在實際應用中,通常將虛擬節點數設置爲 32 甚至更大,因此即使很少的服務節點也能做到相對均勻的數據分佈。


名稱解析

  • CARP,Common Access Redundancy Protocol,即共用地址冗餘協議,能夠使多臺主機共享同一個 IP 地址。在某些配置中,這樣做可以提高可用性,或實現負載均衡。這些主機也可以同時使用其他的不同的 IP 地址。
  • DHT,Distributed Hash Table,即分佈式哈希表,是一種分佈式存儲方法。在不需要服務器的情況下,每個客戶端負責一個小範圍的路由,並負責存儲一小部分數據,從而實現整個 DHT 網絡的尋址和存儲。
  • P2P,Peer to Peer,即對等計算機網絡,是一種在對等者(Peer)之間分配任務和工作負載的分佈式應用架構,是對等計算模型在應用層形成的一種組網或網絡形式。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章