分佈式過程當中咱們將服務分散到若干的節點上,以此經過集體的力量提高服務的目的。然而,對於一個客戶端來講,該由哪一個節點服務呢?或者說對某個節點來講他分配到哪些任務呢?git
考慮到單服務器不能承載,所以使用了分佈式架構,最初的算法爲 hash() mod n, hash()一般取用戶ID,n爲節點數。此方法容易實現且可以知足運營要求。缺點是當單點發生故障時,系統沒法自動恢復。一樣不也不能進行動態增長節點。github
爲了解決單點故障,使用 hash() mod (n/m)
, 算法
這樣任意一個用戶都有 m 個服務器備選,可由 client 隨機選取。緩存
因爲不一樣服務器之間的用戶須要彼此交互,因此全部的服務器須要確切的知道用戶所在的位置。服務器
所以用戶位置被保存到 memcached 中。當一臺發生故障,client 能夠自動切換到對應 backup,因爲切換前另外 1 臺沒有用戶的 session,所以須要 client 自行從新登陸。session
他比強哈希的好處是:解決了單點問題。架構
但存在如下問題:負載不均衡,尤爲是單臺發生故障後剩下一臺會壓力過大;不能動態增刪節點;節點發生故障時須要 client 從新登陸分佈式
一致性 hash 算法提出了在動態變化的 Cache 環境中,斷定哈希算法好壞的四個定義:ide
平衡性是指哈希的結果可以儘量分佈到全部的緩衝中去,這樣可使得全部的緩衝空間都獲得利用。不少哈希算法都可以知足這一條件。memcached
單調性是指若是已經有一些內容經過哈希分派到了相應的緩衝中,又有新的緩衝加入到系統中。哈希的結果應可以保證原有已分配的內容能夠被映射到原有的或者新的緩衝中去,而不會被映射到舊的緩衝集合中的其餘緩衝區。
在分佈式環境中,終端有可能看不到全部的緩衝,而是隻能看到其中的一部分。
當終端但願經過哈希過程將內容映射到緩衝上時,因爲不一樣終端所見的緩衝範圍有可能不一樣,從而致使哈希的結果不一致,最終的結果是相同的內容被不一樣的終端映射到不一樣的緩衝區中。
這種狀況顯然是應該避免的,由於它致使相同內容被存儲到不一樣緩衝中去,下降了系統存儲的效率。分散性的定義就是上述狀況發生的嚴重程度。好的哈希算法應可以儘可能避免不一致的狀況發生,也就是儘可能下降分散性。
負載問題其實是從另外一個角度看待分散性問題。既然不一樣的終端可能將相同的內容映射到不一樣的緩衝區中,那麼對於一個特定的緩衝區而言,也可能被不一樣的用戶映射爲不一樣的內容。
與分散性同樣,這種狀況也是應當避免的,所以好的哈希算法應可以儘可能下降緩衝的負荷。
普通的哈希算法(也稱硬哈希)採用簡單取模的方式,將機器進行散列,這在cache環境不變的狀況下能取得讓人滿意的結果,可是當cache環境動態變化時,
這種靜態取模的方式顯然就不知足單調性的要求(當增長或減小一臺機子時,幾乎全部的存儲內容都要被從新散列到別的緩衝區中)。
一致性哈希算法有多種具體的實現,包括 Chord 算法,KAD 算法等實現,以上的算法的實現都比較複雜。
這裏介紹一種網上廣爲流傳的一致性哈希算法的基本實現原理,感興趣的同窗能夠根據上面的連接或者去網上查詢更詳細的資料。
一致性哈希算法的基本實現原理是將機器節點和key值都按照同樣的hash算法映射到一個0~2^32
的圓環上。
當有一個寫入緩存的請求到來時,計算 Key 值 k 對應的哈希值 Hash(k),若是該值正好對應以前某個機器節點的 Hash 值,則直接寫入該機器節點,
若是沒有對應的機器節點,則順時針查找下一個節點,進行寫入,若是超過 2^32
還沒找到對應節點,則從0開始查找(由於是環狀結構)。
如圖 1 所示:
圖 1 中 Key K 的哈希值在 A 與 B 之間,因而 K 就由節點B來處理。
另外具體機器映射時,還能夠根據處理能力不一樣,將一個實體節點映射到多個虛擬節點。
通過一致性哈希算法散列以後,當有新的機器加入時,將隻影響一臺機器的存儲狀況,
例如新加入的節點H的散列在 B 與 C 之間,則原先由 C 處理的一些數據可能將移至 H 處理,
而其餘全部節點的處理狀況都將保持不變,所以表現出很好的單調性。
而若是刪除一臺機器,例如刪除 C 節點,此時原來由 C 處理的數據將移至 D 節點,而其它節點的處理狀況仍然不變。
而因爲在機器節點散列和緩衝內容散列時都採用了同一種散列算法,所以也很好得下降了分散性和負載。
而經過引入虛擬節點的方式,也大大提升了平衡性。