一致性 hash
分散式過程中我們將服務分散到若干的節點上,以此通過集體的力量提升服務的目的。然而,對於一個客戶端來說,該由哪個節點服務呢?或者說對某個節點來說他分配到哪些任務呢?
強雜湊
考慮到單伺服器不能承載,因此使用了分散式架構,最初的演算法為 hash() mod n, hash()通常取使用者ID,n為節點數。此方法容易實現且能夠滿足運營要求。缺點是當單點發生故障時,系統無法自動恢復。同樣不也不能進行動態增加節點。
弱雜湊
為了解決單點故障,使用 hash() mod (n/m)
,
這樣任意一個使用者都有 m 個伺服器備選,可由 client 隨機選取。
由於不同伺服器之間的使用者需要彼此互動,所以所有的伺服器需要確切的知道使用者所在的位置。
因此使用者位置被儲存到 memcached 中。當一臺發生故障,client 可以自動切換到對應 backup,由於切換前另外 1 臺沒有使用者的 session,因此需要 client 自行重新登入。
- 好處
他比強雜湊的好處是:解決了單點問題。
- 缺點
但存在以下問題:負載不均衡,尤其是單臺發生故障後剩下一臺會壓力過大;不能動態增刪節點;節點發生故障時需要 client 重新登入
一致性 hash 演算法
一致性 hash 演算法提出了在動態變化的 Cache 環境中,判定雜湊演算法好壞的四個定義:
平衡性(Balance)
平衡性是指雜湊的結果能夠儘可能分佈到所有的緩衝中去,這樣可以使得所有的緩衝空間都得到利用。很多雜湊演算法都能夠滿足這一條件。
單調性(Monotonicity)
單調性是指如果已經有一些內容通過雜湊分派到了相應的緩衝中,又有新的緩衝加入到系統中。雜湊的結果應能夠保證原有已分配的內容可以被對映到原有的或者新的緩衝中去,而不會被對映到舊的緩衝集合中的其他緩衝區。
分散性(Spread)
在分散式環境中,終端有可能看不到所有的緩衝,而是隻能看到其中的一部分。
當終端希望通過雜湊過程將內容對映到緩衝上時,由於不同終端所見的緩衝範圍有可能不同,從而導致雜湊的結果不一致,最終的結果是相同的內容被不同的終端對映到不同的緩衝區中。
這種情況顯然是應該避免的,因為它導致相同內容被儲存到不同緩衝中去,降低了系統儲存的效率。分散性的定義就是上述情況發生的嚴重程度。好的雜湊演算法應能夠儘量避免不一致的情況發生,也就是儘量降低分散性。
負載(Load)
負載問題實際上是從另一個角度看待分散性問題。既然不同的終端可能將相同的內容對映到不同的緩衝區中,那麼對於一個特定的緩衝區而言,也可能被不同的使用者對映為不同的內容。
與分散性一樣,這種情況也是應當避免的,因此好的雜湊演算法應能夠儘量降低緩衝的負荷。
普通的雜湊演算法(也稱硬雜湊)採用簡單取模的方式,將機器進行雜湊,這在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 節點,而其它節點的處理情況仍然不變。
而由於在機器節點雜湊和緩衝內容雜湊時都採用了同一種雜湊演算法,因此也很好得降低了分散性和負載。
而通過引入虛擬節點的方式,也大大提高了平衡性。