作者:賀圣軍,叩丁狼高級講師。原創(chuàng)文章,轉(zhuǎn)載請注明出處。
1 場景需求
? 現(xiàn)在實際應(yīng)用中,我們使用redis作為我們的緩存服務(wù)器,假如現(xiàn)在我們的使用三臺redis服務(wù)器用來作為緩存(redis01,redis02,redis03),現(xiàn)在我們需要把我們的數(shù)據(jù)均勻的存放到三個redis服務(wù)器上
? 對于redis服務(wù)器來說,并沒有實現(xiàn)分布式集群,所謂的redis的分布式集群是指由客戶端所決定存數(shù)據(jù)存放到哪臺服務(wù)器以及從哪臺服務(wù)器上取獲取數(shù)據(jù)
- 方案一: 使用我們所謂的hash算法取模,就是把三臺redis服務(wù)器分別編號為0,1,2 然后在客戶端存數(shù)據(jù)的時候,先對key進(jìn)行hash運算出一個具體的hash值,然后hash(object)%3 取余數(shù),
如果余數(shù)為0 那么把數(shù)據(jù)存放到redis01服務(wù)器
如果余數(shù)為1 那么把數(shù)據(jù)存放到redis02服務(wù)器
如果余數(shù)為2 那么把數(shù)據(jù)存放到redis02服務(wù)器
同理,在取數(shù)據(jù)的時候,也是先根據(jù)key進(jìn)行hash值的運算,然后取余找到對應(yīng)的redis服務(wù)器進(jìn)行數(shù)據(jù)的讀取
? 問題:隨著業(yè)務(wù)的增長獲取服務(wù)器的運行狀況,如果出現(xiàn)需要增加服務(wù)器或者說有一個redis服務(wù)器宕機的話,那么按照我們上面的取模運算還可以正常運行嗎?如果不行那么我們有什么樣的方案來解決呢?
2 一致性哈希的概念
? 對于上面的問題,當(dāng)服務(wù)器其出現(xiàn)上/下線的時候,如果還是按照上述的取模運算的話,顯然是會出問題的
- 當(dāng)我們添加一臺新的服務(wù)器的時候,存數(shù)據(jù)的時候,使用的是hash(object)%3 進(jìn)行存的,那么我們在取數(shù)據(jù)的時候使用的是hash(object)%4來判斷服務(wù)器的位置,這樣肯定會取不到數(shù)據(jù)
- 當(dāng)我們減少一臺服務(wù)器的時候,存數(shù)據(jù)的時候,使用的是hash(object)%3 進(jìn)行存的,那么我們在取數(shù)據(jù)的時候使用的是hash(object)%2來判斷服務(wù)器的位置,同樣也取不到數(shù)據(jù)
總是,在我們當(dāng)對服務(wù)器進(jìn)行增減操作的時候,原來的所有的緩存數(shù)據(jù)都需要從新分布排列,這樣帶代價比較大,有沒有一種方法讓我們在增減服務(wù)器的時候,影響的只有一小部分?jǐn)?shù)據(jù),大部分?jǐn)?shù)據(jù)是不需要進(jìn)行改變的。要實現(xiàn)這個功能,就需要使用到一致性哈希算法
? 一致性哈希算法大概實現(xiàn)的思路如下
- 對于所有的hash值按照環(huán)形排列起來,一共有2^32個hash值,暫且成該圓環(huán)為r
- 先把服務(wù)器按照一定的規(guī)則進(jìn)行哈希運算(使用ip或者主機名等),然后在r上找到對應(yīng)的值進(jìn)行標(biāo)記服務(wù)器的位置
- 再把需要存放的redis數(shù)據(jù)庫的值的key進(jìn)行hash運算(key),然后在r上找到對應(yīng)的值進(jìn)行數(shù)據(jù)的標(biāo)記
- 根據(jù)規(guī)則會把key順時針找到第一個服務(wù)器進(jìn)行存儲
- obj1 存放在redis01服務(wù)器
- obj2 存放在redis03服務(wù)器
- obj4 obj3 存放在redis02服務(wù)器
參考上圖,接下來我們分析一下增減服務(wù)器的情況
- 假如redis03 和redis01之間添加一個服務(wù)器redis04,那么影響的數(shù)據(jù)只是redis03到redis04之間的那部分?jǐn)?shù)據(jù),只需要把這部分?jǐn)?shù)據(jù)從redis01中刪除,在redis04中添加即可,其他各個服務(wù)器上的數(shù)據(jù)不會改變
- 假如把redis03刪除掉,那么原來存放在redis03中的數(shù)據(jù)全部遷移到redis01服務(wù)器,對于原來的redis02和redis01中的數(shù)據(jù)不需要發(fā)生改變
通過上面這種解決方案,基本上可以解決分布式的一致性哈希的問題了,但是由于上面這種情況也會出翔一些情況
- 如果服務(wù)器的節(jié)點夠少,只有redis01 和redis03 那么上面的四個對象有obj1存放在redis01服務(wù)器,obj2,obj3,obj4都是存放在redis03服務(wù)器,這樣就會造成資源的分布不均,資源利用率不平衡
- 如果服務(wù)器redis01,redis02,redis03 在正常的情況下已經(jīng)是負(fù)載均衡的了,那么突然redis02宕機,那么會把所有的redis02上面的數(shù)據(jù)都會王redis03上遷移,有可能因為資源的耗盡導(dǎo)致redis03也宕機,接下來導(dǎo)致redis01也宕機
對于上面兩種情況,我們需要使用所謂的一個虛擬節(jié)點的概念,在hash圓環(huán)上創(chuàng)建足夠多的虛擬節(jié)點,然后把虛擬節(jié)點和物理節(jié)點映射起來,這樣可以保證資源的平衡性
對于當(dāng)一個服務(wù)器redis02宕機后,應(yīng)該把redis02上的資源均勻的分布到redis01和redis03上面,這樣可以保證各個服務(wù)器的負(fù)載均衡
3 一致性哈希的特點
1、平衡性(Balance):平衡性是指哈希的結(jié)果能夠盡可能分布到所有的緩沖中去,這樣可以使得所有的緩沖空間都得到利用。很多哈希算法都能夠滿足這一條件。
2、分散性(Spread) :在分布式環(huán)境中,終端有可能看不到所有的緩沖,而是只能看到其中的一部分。當(dāng)終端希望通過哈希過程將內(nèi)容映射到緩沖上時,由于不同終端所見的緩沖范圍有可能不同,從而導(dǎo)致哈希的結(jié)果不一致,最終的結(jié)果是相同的內(nèi)容被不同的終端映射到不同的緩沖區(qū)中。這種情況顯然是應(yīng)該避免的,因為它導(dǎo)致相同內(nèi)容被存儲到不同緩沖中去,降低了系統(tǒng)存儲的效率。分散性的定義就是上述情況發(fā)生的嚴(yán)重程度。好的哈希算法應(yīng)能夠盡量避免不一致的情況發(fā)生,也就是盡量降低分散性。
3、負(fù)載(Load):負(fù)載問題實際上是從另一個角度看待分散性問題。既然不同的終端可能將相同的內(nèi)容映射到不同的緩沖區(qū)中,那么對于一個特定的緩沖區(qū)而言,也可能被不同的用戶映射為不同 的內(nèi)容。與分散性一樣,這種情況也是應(yīng)當(dāng)避免的,因此好的哈希算法應(yīng)能夠盡量降低緩沖的負(fù)荷。
4、單調(diào)性(Monotonicity):單調(diào)性是指如果已經(jīng)有一些內(nèi)容通過哈希分派到了相應(yīng)的緩沖中,又有新的緩沖加入到系統(tǒng)中。哈希的結(jié)果應(yīng)能夠保證原有已分配的內(nèi)容可以被映射到原有的或者新的緩沖中去,而不會被映射到舊的緩沖集合中的其他緩沖區(qū)。