分布式缓存——一致性哈希算法
本文主要來自:http://www.zsythink.net/archives/1182
摘錄防丟失
一致性哈希算法定義
一致性哈希算法在1997年由麻省理工學(xué)院提出的一種分布式哈希(DHT)實現(xiàn)算法,設(shè)計目標是為了解決因特網(wǎng)中的熱點(Hot spot)問題,初衷和CARP(Common Access Redundancy Protocol,共用地址冗余協(xié)議)十分類似。一致性哈希修正了CARP使用的簡 單哈希算法帶來的問題,使得分布式哈希(DHT,Distributed Hash Table)可以在P2P環(huán)境中真正得到應(yīng)用。
一致性hash算法提出了在動態(tài)變化的Cache環(huán)境中,判定哈希算法好壞的四個定義:
平衡性(Balance):平衡性是指哈希的結(jié)果能夠盡可能分布到所有的緩沖中去,這樣可以使得所有的緩沖空間都得到利用。很多哈希算法都能夠滿足這一條件。
單調(diào)性(Monotonicity):單調(diào)性是指如果已經(jīng)有一些內(nèi)容通過哈希分派到了相應(yīng)的緩沖中,又有新的緩沖加入到系統(tǒng)中。哈希的結(jié)果應(yīng)能夠保證原有已分配的內(nèi)容可以被映射到原有的或者新的緩沖中去,而不會被映射到舊的緩沖集合中的其他緩沖區(qū)。
分散性(Spread):在分布式環(huán)境中,終端有可能看不到所有的緩沖,而是只能看到其中的一部分。當(dāng)終端希望通過哈希過程將內(nèi)容映射到緩沖上時,由于不同終端所見的緩沖范圍有可能不同,從而導(dǎo)致哈希的結(jié)果不一致,最終的結(jié)果是相同的內(nèi)容被不同的終端映射到不同的緩沖區(qū)中。這種情況顯然是應(yīng)該避免的,因為它導(dǎo)致相同內(nèi)容被存儲到不同緩沖中去,降低了系統(tǒng)存儲的效率。分散性的定義就是上述情況發(fā)生的嚴重程度。好的哈希算法應(yīng)能夠盡量避免不一致的情況發(fā)生,也就是盡量降低分散性。
負載(Load):負載問題實際上是從另一個角度看待分散性問題。既然不同的終端可能將相同的內(nèi)容映射到不同的緩沖區(qū)中,那么對于一個特定的緩沖區(qū)而言,也可能被不同的用戶映射為不同 的內(nèi)容。與分散性一樣,這種情況也是應(yīng)當(dāng)避免的,因此好的哈希算法應(yīng)能夠盡量降低緩沖的負荷。
在分布式集群中,對機器的添加刪除,或者機器故障后自動脫離集群這些操作是分布式集群管理最基本的功能。如果采用常用的hash(object)%N算法,那么在有機器添加或者刪除后,很多原有的數(shù)據(jù)就無法找到了,這樣嚴重的違反了單調(diào)性原則。
在了解一致性哈希算法之前,最好先了解一下緩存中的一個應(yīng)用場景,了解了這個應(yīng)用場景之后,再來理解一致性哈希算法,就容易多了,也更能體現(xiàn)出一致性哈希算法的優(yōu)點,那么,我們先來描述一下這個經(jīng)典的分布式緩存的應(yīng)用場景。
場景描述
假設(shè),我們有三臺緩存服務(wù)器,用于緩存圖片,我們?yōu)檫@三臺緩存服務(wù)器編號為0號、1號、2號,現(xiàn)在,有3萬張圖片需要緩存,我們希望這些圖片被均勻的緩存到這3臺服務(wù)器上,以便它們能夠分攤緩存的壓力。也就是說,我們希望每臺服務(wù)器能夠緩存1萬張左右的圖片,那么,我們應(yīng)該怎樣做呢?如果我們沒有任何規(guī)律的將3萬張圖片平均的緩存在3臺服務(wù)器上,可以滿足我們的要求嗎?可以!但是如果這樣做,當(dāng)我們需要訪問某個緩存項時,則需要遍歷3臺緩存服務(wù)器,從3萬個緩存項中找到我們需要訪問的緩存,遍歷的過程效率太低,時間太長,當(dāng)我們找到需要訪問的緩存項時,時長可能是不能被接收的,也就失去了緩存的意義,緩存的目的就是提高速度,改善用戶體驗,減輕后端服務(wù)器壓力,如果每次訪問一個緩存項都需要遍歷所有緩存服務(wù)器的所有緩存項,想想就覺得很累,那么,我們該怎么辦呢?原始的做法是對緩存項的鍵進行哈希,將hash后的結(jié)果對緩存服務(wù)器的數(shù)量進行取模操作,通過取模后的結(jié)果,決定緩存項將會緩存在哪一臺服務(wù)器上,這樣說可能不太容易理解,我們舉例說明,仍然以剛才描述的場景為例,假設(shè)我們使用圖片名稱作為訪問圖片的key,假設(shè)圖片名稱是不重復(fù)的,那么,我們可以使用如下公式,計算出圖片應(yīng)該存放在哪臺服務(wù)器上。
hash(圖片名稱)% N
因為圖片的名稱是不重復(fù)的,所以,當(dāng)我們對同一個圖片名稱做相同的哈希計算時,得出的結(jié)果應(yīng)該是不變的,如果我們有3臺服務(wù)器,使用哈希后的結(jié)果對3求余,那么余數(shù)一定是0、1或者2,沒錯,正好與我們之前的服務(wù)器編號相同,如果求余的結(jié)果為0, 我們就把當(dāng)前圖片名稱對應(yīng)的圖片緩存在0號服務(wù)器上,如果余數(shù)為1,就把當(dāng)前圖片名對應(yīng)的圖片緩存在1號服務(wù)器上,如果余數(shù)為2,同理,那么,當(dāng)我們訪問任意一個圖片的時候,只要再次對圖片名稱進行上述運算,即可得出對應(yīng)的圖片應(yīng)該存放在哪一臺緩存服務(wù)器上,我們只要在這一臺服務(wù)器上查找圖片即可,如果圖片在對應(yīng)的服務(wù)器上不存在,則證明對應(yīng)的圖片沒有被緩存,也不用再去遍歷其他緩存服務(wù)器了,通過這樣的方法,即可將3萬張圖片隨機的分布到3臺緩存服務(wù)器上了,而且下次訪問某張圖片時,直接能夠判斷出該圖片應(yīng)該存在于哪臺緩存服務(wù)器上,這樣就能滿足我們的需求了,我們暫時稱上述算法為HASH算法或者取模算法,取模算法的過程可以用下圖表示。
但是,使用上述HASH算法進行緩存時,會出現(xiàn)一些缺陷,試想一下,如果3臺緩存服務(wù)器已經(jīng)不能滿足我們的緩存需求,那么我們應(yīng)該怎么做呢?沒錯,很簡單,多增加兩臺緩存服務(wù)器不就行了,假設(shè),我們增加了一臺緩存服務(wù)器,那么緩存服務(wù)器的數(shù)量就由3臺變成了4臺,此時,如果仍然使用上述方法對同一張圖片進行緩存,那么這張圖片所在的服務(wù)器編號必定與原來3臺服務(wù)器時所在的服務(wù)器編號不同,因為除數(shù)由3變?yōu)榱?,被除數(shù)不變的情況下,余數(shù)肯定不同,這種情況帶來的結(jié)果就是當(dāng)服務(wù)器數(shù)量變動時,所有緩存的位置都要發(fā)生改變,換句話說,當(dāng)服務(wù)器數(shù)量發(fā)生改變時,所有緩存在一定時間內(nèi)是失效的,當(dāng)應(yīng)用無法從緩存中獲取數(shù)據(jù)時,則會向后端服務(wù)器請求數(shù)據(jù),同理,假設(shè)3臺緩存中突然有一臺緩存服務(wù)器出現(xiàn)了故障,無法進行緩存,那么我們則需要將故障機器移除,但是如果移除了一臺緩存服務(wù)器,那么緩存服務(wù)器數(shù)量從3臺變?yōu)?臺,如果想要訪問一張圖片,這張圖片的緩存位置必定會發(fā)生改變,以前緩存的圖片也會失去緩存的作用與意義,由于大量緩存在同一時間失效,造成了緩存的雪崩,此時前端緩存已經(jīng)無法起到承擔(dān)部分壓力的作用,后端服務(wù)器將會承受巨大的壓力,整個系統(tǒng)很有可能被壓垮,所以,我們應(yīng)該想辦法不讓這種情況發(fā)生,但是由于上述HASH算法本身的緣故,使用取模法進行緩存時,這種情況是無法避免的,為了解決這些問題,一致性哈希算法誕生了。
我們來回顧一下使用上述算法會出現(xiàn)的問題。
問題1:當(dāng)緩存服務(wù)器數(shù)量發(fā)生變化時,會引起緩存的雪崩,可能會引起整體系統(tǒng)壓力過大而崩潰(大量緩存同一時間失效)。
問題2:當(dāng)緩存服務(wù)器數(shù)量發(fā)生變化時,幾乎所有緩存的位置都會發(fā)生改變,怎樣才能盡量減少受影響的緩存呢?
其實,上面兩個問題是一個問題,那么,一致性哈希算法能夠解決上述問題嗎?
我們現(xiàn)在就來了解一下一致性哈希算法。
一致性哈希算法的基本概念
其實,一致性哈希算法也是使用取模的方法,只是,剛才描述的取模法是對服務(wù)器的數(shù)量進行取模,而一致性哈希算法是對2^32取模,什么意思呢?我們慢慢聊。
首先,我們把二的三十二次方想象成一個圓,就像鐘表一樣,鐘表的圓可以理解成由60個點組成的圓,而此處我們把這個圓想象成由2^32個點組成的圓,示意圖如下:
圓環(huán)的正上方的點代表0,0點右側(cè)的第一個點代表1,以此類推,2、3、4、5、6……直到232-1,也就是說0點左側(cè)的第一個點代表232-1
我們把這個由2的32次方個點組成的圓環(huán)稱為hash環(huán)。
那么,一致性哈希算法與上圖中的圓環(huán)有什么關(guān)系呢?我們繼續(xù)聊,仍然以之前描述的場景為例,假設(shè)我們有3臺緩存服務(wù)器,服務(wù)器A、服務(wù)器B、服務(wù)器C,那么,在生產(chǎn)環(huán)境中,這三臺服務(wù)器肯定有自己的IP地址,我們使用它們各自的IP地址進行哈希計算,使用哈希后的結(jié)果對2^32取模,可以使用如下公式示意。
hash(服務(wù)器A的IP地址) % 2^32
通過上述公式算出的結(jié)果一定是一個0到232-1之間的一個整數(shù),我們就用算出的這個整數(shù),代表服務(wù)器A,既然這個整數(shù)肯定處于0到232-1之間,那么,上圖中的hash環(huán)上必定有一個點與這個整數(shù)對應(yīng),而我們剛才已經(jīng)說明,使用這個整數(shù)代表服務(wù)器A,那么,服務(wù)器A就可以映射到這個環(huán)上,用下圖示意
同理,服務(wù)器B與服務(wù)器C也可以通過相同的方法映射到上圖中的hash環(huán)中
hash(服務(wù)器B的IP地址) % 2^32
hash(服務(wù)器C的IP地址) % 2^32
通過上述方法,可以將服務(wù)器B與服務(wù)器C映射到上圖中的hash環(huán)上,示意圖如下
假設(shè)3臺服務(wù)器映射到hash環(huán)上以后如上圖所示(當(dāng)然,這是理想的情況,我們慢慢聊)。
好了,到目前為止,我們已經(jīng)把緩存服務(wù)器與hash環(huán)聯(lián)系在了一起,我們通過上述方法,把緩存服務(wù)器映射到了hash環(huán)上,那么使用同樣的方法,我們也可以將需要緩存的對象映射到hash環(huán)上。
假設(shè),我們需要使用緩存服務(wù)器緩存圖片,而且我們?nèi)匀皇褂脠D片的名稱作為找到圖片的key,那么我們使用如下公式可以將圖片映射到上圖中的hash環(huán)上。
hash(圖片名稱) % 2^32
映射后的示意圖如下,下圖中的橘黃色圓形表示圖片
好了,現(xiàn)在服務(wù)器與圖片都被映射到了hash環(huán)上,那么上圖中的這個圖片到底應(yīng)該被緩存到哪一臺服務(wù)器上呢?上圖中的圖片將會被緩存到服務(wù)器A上,為什么呢?因為從圖片的位置開始,沿順時針方向遇到的第一個服務(wù)器就是A服務(wù)器,所以,上圖中的圖片將會被緩存到服務(wù)器A上,如下圖所示。
沒錯,一致性哈希算法就是通過這種方法,判斷一個對象應(yīng)該被緩存到哪臺服務(wù)器上的,將緩存服務(wù)器與被緩存對象都映射到hash環(huán)上以后,從被緩存對象的位置出發(fā),沿順時針方向遇到的第一個服務(wù)器,就是當(dāng)前對象將要緩存于的服務(wù)器,由于被緩存對象與服務(wù)器hash后的值是固定的,所以,在服務(wù)器不變的情況下,一張圖片必定會被緩存到固定的服務(wù)器上,那么,當(dāng)下次想要訪問這張圖片時,只要再次使用相同的算法進行計算,即可算出這個圖片被緩存在哪個服務(wù)器上,直接去對應(yīng)的服務(wù)器查找對應(yīng)的圖片即可。
剛才的示例只使用了一張圖片進行演示,假設(shè)有四張圖片需要緩存,示意圖如下
1號、2號圖片將會被緩存到服務(wù)器A上,3號圖片將會被緩存到服務(wù)器B上,4號圖片將會被緩存到服務(wù)器C上。
一致性哈希算法的優(yōu)點
經(jīng)過上述描述,我想兄弟你應(yīng)該已經(jīng)明白了一致性哈希算法的原理了,但是話說回來,一致性哈希算法能夠解決之前出現(xiàn)的問題嗎,我們說過,如果簡單的對服務(wù)器數(shù)量進行取模,那么當(dāng)服務(wù)器數(shù)量發(fā)生變化時,會產(chǎn)生緩存的雪崩,從而很有可能導(dǎo)致系統(tǒng)崩潰,那么使用一致性哈希算法,能夠避免這個問題嗎?我們來模擬一遍,即可得到答案。
假設(shè),服務(wù)器B出現(xiàn)了故障,我們現(xiàn)在需要將服務(wù)器B移除,那么,我們將上圖中的服務(wù)器B從hash環(huán)上移除即可,移除服務(wù)器B以后示意圖如下。
在服務(wù)器B未移除時,圖片3應(yīng)該被緩存到服務(wù)器B中,可是當(dāng)服務(wù)器B移除以后,按照之前描述的一致性哈希算法的規(guī)則,圖片3應(yīng)該被緩存到服務(wù)器C中,因為從圖片3的位置出發(fā),沿順時針方向遇到的第一個緩存服務(wù)器節(jié)點就是服務(wù)器C,也就是說,如果服務(wù)器B出現(xiàn)故障被移除時,圖片3的緩存位置會發(fā)生改變
但是,圖片4仍然會被緩存到服務(wù)器C中,圖片1與圖片2仍然會被緩存到服務(wù)器A中,這與服務(wù)器B移除之前并沒有任何區(qū)別,這就是一致性哈希算法的優(yōu)點,如果使用之前的hash算法,服務(wù)器數(shù)量發(fā)生改變時,所有服務(wù)器的所有緩存在同一時間失效了,而使用一致性哈希算法時,服務(wù)器的數(shù)量如果發(fā)生改變,并不是所有緩存都會失效,而是只有部分緩存會失效,前端的緩存仍然能分擔(dān)整個系統(tǒng)的壓力,而不至于所有壓力都在同一時間集中到后端服務(wù)器上。
這就是一致性哈希算法所體現(xiàn)出的優(yōu)點。
hash環(huán)的偏斜
在介紹一致性哈希的概念時,我們理想化的將3臺服務(wù)器均勻的映射到了hash環(huán)上,如下圖所示
但是,理想很豐滿,現(xiàn)實很骨感,我們想象的與實際情況往往不一樣。
在實際的映射中,服務(wù)器可能會被映射成如下模樣。
聰明如你一定想到了,如果服務(wù)器被映射成上圖中的模樣,那么被緩存的對象很有可能大部分集中緩存在某一臺服務(wù)器上,如下圖所示。
上圖中,1號、2號、3號、4號、6號圖片均被緩存在了服務(wù)器A上,只有5號圖片被緩存在了服務(wù)器B上,服務(wù)器C上甚至沒有緩存任何圖片,如果出現(xiàn)上圖中的情況,A、B、C三臺服務(wù)器并沒有被合理的平均的充分利用,緩存分布的極度不均勻,而且,如果此時服務(wù)器A出現(xiàn)故障,那么失效緩存的數(shù)量也將達到最大值,在極端情況下,仍然有可能引起系統(tǒng)的崩潰,上圖中的情況則被稱之為hash環(huán)的偏斜,那么,我們應(yīng)該怎樣防止hash環(huán)的偏斜呢?一致性hash算法中使用"虛擬節(jié)點"解決了這個問題,我們繼續(xù)聊。
虛擬節(jié)點
話接上文,由于我們只有3臺服務(wù)器,當(dāng)我們把服務(wù)器映射到hash環(huán)上的時候,很有可能出現(xiàn)hash環(huán)偏斜的情況,當(dāng)hash環(huán)偏斜以后,緩存往往會極度不均衡的分布在各服務(wù)器上,聰明如你一定已經(jīng)想到了,如果想要均衡的將緩存分布到3臺服務(wù)器上,最好能讓這3臺服務(wù)器盡量多的、均勻的出現(xiàn)在hash環(huán)上,但是,真實的服務(wù)器資源只有3臺,我們怎樣憑空的讓它們多起來呢,沒錯,就是憑空的讓服務(wù)器節(jié)點多起來,既然沒有多余的真正的物理服務(wù)器節(jié)點,我們就只能將現(xiàn)有的物理節(jié)點通過虛擬的方法復(fù)制出來,這些由實際節(jié)點虛擬復(fù)制而來的節(jié)點被稱為"虛擬節(jié)點"。加入虛擬節(jié)點以后的hash環(huán)如下。
“虛擬節(jié)點"是"實際節(jié)點”(實際的物理服務(wù)器)在hash環(huán)上的復(fù)制品,一個實際節(jié)點可以對應(yīng)多個虛擬節(jié)點。
從上圖可以看出,A、B、C三臺服務(wù)器分別虛擬出了一個虛擬節(jié)點,當(dāng)然,如果你需要,也可以虛擬出更多的虛擬節(jié)點。引入虛擬節(jié)點的概念后,緩存的分布就均衡多了,上圖中,1號、3號圖片被緩存在服務(wù)器A中,5號、4號圖片被緩存在服務(wù)器B中,6號、2號圖片被緩存在服務(wù)器C中,如果你還不放心,可以虛擬出更多的虛擬節(jié)點,以便減小hash環(huán)偏斜所帶來的影響,虛擬節(jié)點越多,hash環(huán)上的節(jié)點就越多,緩存被均勻分布的概率就越大。
總結(jié)
以上是生活随笔為你收集整理的分布式缓存——一致性哈希算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDFS分布式文件系统设计原理
- 下一篇: 未读