1. 程式人生 > 其它 >一致性雜湊演算法的golang實現

一致性雜湊演算法的golang實現

1. 一致性雜湊演算法

關於一致性雜湊演算法的詳解,可以參見這篇部落格文章

簡單來說,一致性雜湊通過雜湊環和增加虛擬節點來解決節點變更時的大範圍資料遷移和冷熱不均的問題

一致性雜湊的一個開源實現為:stathat.com/c/consistent

1.1 資料遷移

我們首先從資料遷移的角度來討論雜湊演算法和一致性雜湊演算法,假設我們當前有3個節點,要新增一個節點變為4個節點

對於雜湊演算法來說,一個key是大概率發生遷移的,因為key%3不太可能等於key%4,並且遷移的節點涉及到叢集所有的節點,很可能因為資料遷移導致整個叢集不可用

而對於一致性雜湊演算法來講,當新增節點時,資料遷移只會發生在新增節點和它順時針的後一個節點之間,所涉及的資料也僅僅是新增節點和它逆時針的前一個節點之間的資料

在資料遷移方面,一致性雜湊保證了少數資料遷移和少數節點參與資料遷移,極大地提升了系統的穩定性

下面反映的是使用雜湊演算法,在新增節點的情況下,資料遷移率的變化

可以很明顯的看到,當叢集規模變大時,新增/刪除一個節點對系統帶來的影響將是致命的,90%以上的資料都將被遷移

$ go run ./hash.go -keys 10000000 -nodes 3 -new-nodes 4
74.999980%

$ go run ./hash.go -keys 10000000 -nodes 10 -new-nodes 11
90.909000%

$ go run ./hash.go -keys 10000000 -nodes 20 -new-nodes 21 
95.238000%

下面反映的是使用一致性雜湊,在新增節點的情況下,資料遷移率的變化

可以很明顯的看到,隨著叢集規模的變大,新增/刪除一個節點對系統帶來的影響越小,遷移的資料量將會越少

$ go run ./consistent-hash.go -keys 10000000 -nodes 3 -new-nodes 4
24.301550%

$ go run ./consistent-hash.go -keys 10000000 -nodes 10 -new-nodes 11
6.479330%

$ go run ./consistent-hash.go -keys 10000000 -nodes 20 -new-nodes 21
4.928480%

1.2 冷熱不均

什麼是冷熱不均問題呢?在一致性雜湊中,我們將節點通過一定的雜湊規則對映為雜湊環上的一系列點

因為我們的資料讀寫規則是一個key在雜湊環上的對映,沿著順時針遇到的第一個節點就是該key的儲存節點

設想這樣一個場景:雜湊環上所有的節點對映都集中在一片區域,這就導致了第一個節點將承受巨大的訪問壓力,這就是節點間的冷熱不均現象

如何解決冷熱不均?引入虛擬節點,將一個儲存例項對映為多個虛擬節點,分佈在雜湊環上,藉此解決冷熱不均問題

在下面的模擬中我們可以看到普通的一致性雜湊和帶虛擬節點的一致性雜湊演算法的節點訪問比例

很明顯,我們可以看到帶虛擬節點的一致性雜湊平衡了各個節點之間的訪問比例,解決了冷熱不均問題

$ go run ./consistent-hash-vnode.go -keys 10000000 -nodes 3 -vnode 100
normal mode: node0 79.677728% , node1 87.908229% , node2 132.414073%
vnode mode: node0 102.326440% , node1 95.905930% , node2 101.767660%

$ go run ./consistent-hash-vnode.go -keys 10000000 -nodes 3 -vnode 200
normal mode: node0 79.677728% , node1 87.908229% , node2 132.414073%
vnode mode: node0 100.630600% , node1 107.318141% , node2 92.051289%

2. golang實現一致性雜湊

最後我們簡單看一下文中使用的一致性雜湊開源實現stathat.com/c/consistent

其核心方法就是如下幾個

// 初始化一致性物件
cons := consistent.New()

// 新增節點
cons.Add("cacheA")

// 刪除節點
cons.Remove("cacheB")

// 獲取資料儲存的節點資訊
server, err := cons.Get("user_89138238")

非常簡單的實現,利用雜湊和節點之間的對映就可以做到

參考:

https://github.com/stathat/consistent/blob/master/consistent.go

https://time.geekbang.org/column/article/207426