一直性Hash算法在很多場景下都有應用,尤其是在分布式緩存系統中,經常用其來進行緩存的訪問的負載均衡,比如:redis等<k,v>非關系數據庫作為緩存系統。我們首先來看一下采用取模方式進行緩存的問題。
一致性Hash算法的使用場景
假設我們的將10台redis部署為我們的緩存系統,存儲<k,v>數據,存儲方式是:hash(k)%10,用來將數據分散到各個redis存儲系統中。這樣做,最大的問題就在於:如果此緩存系統擴展(比如:增加或減少redis服務器的數量),節點故障宕機等將會帶來很高的代價。比如:我們業務量增大了,需要擴展我們的緩存系統,再增加一台redis作為緩存服務器,那么后來的數據<k,v>的散列方式變為了:hash(k)%11。我們可以看到,如果我們要查找擴展之前的數據,利用hash(k)%11,則會找不到對應的存儲服務器。所以這個時候大量的數據失效了(訪問不到了)。
這時候,我們就要進行數據的重現散列,如果是將redis作為存儲系統,則需要進行數據遷移,然后進行恢復,但是這個時候就意味着每次增減服務器的時候,集群就需要大量的通信,進行數據遷移,這個開銷是非常大的。如果只是緩存,那么緩存就都失效了。這會形成緩存擊穿,導致數據庫壓力巨大,可能會導致應用的崩潰。
一致性Hash算法的原理
因為對於hash(k)的范圍在int范圍,所以我們將0~2^32作為一個環。其步驟為:
1,求出每個服務器的hash(服務器ip)值,將其配置到一個 0~2^n 的圓環上(n通常取32)。
2,用同樣的方法求出待存儲對象的主鍵 hash值,也將其配置到這個圓環上,然后從數據映射到的位置開始順時針查找,將數據分布到找到的第一個服務器節點上。
其分布如圖:
這是一致性hash算法的基本原理,接下來我們看一下,此算法是如何解決 我們上邊 說的 緩存系統的擴展或者節點宕機導致的緩存失效的問題。比如:再加入一個redis節點:
如上圖,當我們加入redis node5之后,影響的范圍只有黃色標出的那部分,不會造成全局的變動。
除了上邊的優點,其實還有一個優點:對於熱點數據,如果發現node1訪問量明顯很大,負載高於其他節點,這就說明node1存儲的數據是熱點數據。這時候,為了減少node1的負載,我們可以在熱點數據位置再加入一個node,用來分擔熱點數據的壓力。
雪崩效應
接下來我們來看一下,當有節點宕機時會有什么問題。如下圖:
如上圖,當B節點宕機后,原本存儲在B節點的k1,k2將會遷移到節點C上,這可能會導致很大的問題。如果B上存儲的是熱點數據,將數據遷移到C節點上,然后C需要承受B+C的數據,也承受不住,也掛了。。。。然后繼續CD都掛了。這就造成了雪崩效應。
上面會造成雪崩效應的原因分析:
如果不存在熱點數據的時候,每台機器的承受的壓力是M/2(假設每台機器的最高負載能力為M),原本是不會有問題的,但是,這個時候A服務器由於有熱點數據掛了,然后A的數據遷移至B,導致B所需要承受的壓力變為M(還不考慮熱點數據訪問的壓力),所以這個失敗B是必掛的,然后C至少需要承受1.5M的壓力。。。。然后大家一起掛。。。
所以我們通過上面可以看到,之所以會大家一起掛,原因在於如果一台機器掛了,那么它的壓力全部被分配到一台機器上,導致雪崩。
怎么解決雪崩問題呢,這時候需要引入虛擬節點來進行解決。
虛擬節點
虛擬節點,我們可以針對每個實際的節點,虛擬出多個虛擬節點,用來映射到圈上的位置,進行存儲對應的數據。如下圖:
如上圖:A節點對應A1,A2,BCD節點同理。這時候,如果A節點掛了,A節點的數據遷移情況是:A1數據會遷移到C2,A2數據遷移到D1。這就相當於A的數據被C和D分擔了,這就避免了雪崩效應的發送,而且虛擬節點我們可以自定義設置,使其適用於我們的應用。