一致性哈希(Consistent Hashing)

筆者先拿普通哈希和redis集群舉個例子。服務端通常會通過計算發送的請求的某個字段的哈希值,來決定這個請求會被轉發到哪個redis實例,如圖Figure1所示。

一致性哈希(Consistent Hashing)

Figure1

圖中,假設我們有三臺redis實例,假設這個服務通過計算發送請求的客戶端的IP地址,來決定查詢哪個redis實例。請求到來時,先計算出hash值,假設計算出的hash值為十,然後模三,得到的值為一,這個請求將會查詢編號為2的那個redis實例。然而,此時發生了一個情況,一臺新的redis實例被添加進來了,此時,依據原來的方法,計算出hash值為十,模四,得到的值為二,此時請求就會去編號為3的redis實例,當然,這個時候是無法命中緩存的,因為編號為3的redis實例,並沒有這條緩存。如果在併發量極高的情況下,出現了這種case,那麼後果就是緩存全部無法命中,大量的請求就會直接請求到數據庫,然後就是緩存雪崩了。

真實的生產環境中,這種case是肯定不允許出現的,那麼如何避免呢?此時使用的便是一致性哈希。

一致性哈希(Consistent Hashing)

Figure2

一致性哈希是一個環,環上的數字是0~2^32-1,也就是無符號整形的範圍,計算出來的hash值一定會落在這個範圍之內。如圖Figure2所示,此處引用一張來自baidu的圖片,該圓環沿著順時針方向,數字增長。用於標識redis實例的值,均勻地分佈在這個圓環上。假設對一個請求的某個字段,計算出了其hash值,該值落在了Figure2圖中的節點A和節點B之間,那麼,該請求就會沿著順時針方向尋找節點,它找到了節點B,這個節點也就是它所要查詢的緩存所在的節點。

那麼,一致性哈希是如何避免redis實例數量改變引發緩存失效的呢?

還是Figure2那張圖,我們假設,節點D因為某些原因,失效了。那麼根據一致性哈希的原理,我們發現,原先落在節點C和節點D之間的請求的緩存,會全部失效,因為它們按照順時針方向,找到了節點A,而節點A中並沒有這部分請求的緩存。此時,我們再來觀察一下其它的請求。我們發現,A和B之間的請求仍然會命中B節點,B和C之間的請求仍然會命中C節點,D失效前D和A之間的請求仍然會命中A節點。也就是說,除了C和D之間的請求緩存失效之外,其它部分均為受到任何影響。這種數據結構很好的幫我們解決了普通哈希因為實例數量改變引發的失效問題。採用一致性哈希,會使得系統的容錯性和可擴展性變得非常好。

一致性哈希的一個關鍵點在於,在哈希環上,節點的分佈需要均勻。如果節點分佈得不均勻,就會發生數據傾斜,如圖Figure3所示。

一致性哈希(Consistent Hashing)

Figure3

如果一致性哈希環上節點分佈嚴重不均勻,假設某個承載了大量緩存的節點失效了,那麼同樣會導致很嚴重的後果。因此一致性哈希,也需要合理的使用方式,才能發揮它的作用。


分享到:


相關文章: