您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“如何用Java實(shí)現(xiàn)一致性Hash算法”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
現(xiàn)在通常使用Redis來做分布式緩存,下面我們就以Redis為例:
假如當(dāng)前我們系統(tǒng)的業(yè)務(wù)發(fā)展很快,需要緩存的數(shù)據(jù)很多,所以我們做了一個(gè)由三組主從復(fù)制的redis組成的高可用的redis集群,如何將請求路由的不同的redis集群上,這是我們需要考慮的,常用的路由算法:
隨機(jī)算法:每次將請求隨機(jī)的發(fā)送到其中一組Redis集群中,這種算法的好處是請求會被均勻的分發(fā)到每組Redis集群上;缺點(diǎn)也很明顯,由于隨機(jī)分發(fā)請求,為了提高緩存的命中率,所以同一份數(shù)據(jù)需要在每組集群中都存在,這樣就會造成了數(shù)據(jù)的冗余,浪費(fèi)了存儲空間
Hash算法:針對隨機(jī)算法的問題,我們可以考慮Hash算法,舉例: 現(xiàn)在有三組redis集群,我們可以對每次緩存key的hash值取模,公式:index=hash(key) % 3
,index的值就對應(yīng)著3組集群,這樣就可以保證同一個(gè)請求每次都被分發(fā)到同一個(gè)redis集群上,無需對數(shù)據(jù)做冗余,完美的解決了剛才隨機(jī)算法的缺點(diǎn);
但是hash算法也有缺點(diǎn):對于容錯性和伸縮性支持很差,舉例:當(dāng)我們?nèi)Mredis集群中其中一組節(jié)點(diǎn)宕機(jī)了,那么此時(shí)的redis集群中可用的數(shù)量變成了2,公式變成了index=hash(key) % 2
, 所有數(shù)據(jù)緩存的節(jié)點(diǎn)位置就發(fā)生了變化,造成緩存的命中率直線下降;
同理,當(dāng)我們需要擴(kuò)展一組新的redis機(jī)器,計(jì)算的公式index=hash(key) % 4
,大量的key會被重新定位到其他服務(wù)器,也會造成緩存的命中率下降。
為了解決hash算法容錯性和伸縮性的問題,一致性hash算法由此而生~
先構(gòu)造一個(gè)長度為2^32-1的整數(shù)環(huán)(稱為一致性hash環(huán)),然后給每組redis集群命名,根據(jù)名字的hash值計(jì)算出每組集群應(yīng)該放在什么位置
根據(jù)緩存數(shù)據(jù)的key計(jì)算出hash值,計(jì)算出出來的hash值同樣也分布在一致性hash環(huán)上; 假如現(xiàn)在有5個(gè)數(shù)據(jù)需要緩存對應(yīng)的key分別為key1、key2、key3、key4、key5,計(jì)算hash值之后的分部如下圖
然后順著hash環(huán)順時(shí)針方向查找reids集群,把數(shù)據(jù)存放到最近的集群上
最后所有key4、key5存放在了集群2,key1、key3存放在了集群1,key2存放在了集群3
還是繼續(xù)沿用上面的例子,我們來看下一致性哈希算法的容錯性如何呢?假如其中 集群1 跪了,那么影響的數(shù)據(jù)只有key1和key3,其他數(shù)據(jù)存放的位置不受影響;當(dāng)再次緩存key1、key3的時(shí)候根據(jù)順時(shí)針查找,會把數(shù)據(jù)存放到集群3上面
如果我們需要在當(dāng)前的基礎(chǔ)上再添加一組redis集群4,根據(jù)名字hash之后的位置在集群1和集群2之間
新加redis集群4之后影響的只有key1數(shù)據(jù),其他數(shù)據(jù)不受影響。
經(jīng)過容錯性、伸縮性的驗(yàn)證證明了一致性哈希算法確實(shí)能解決Hash算法的問題,但是現(xiàn)在的算法就是完美的嗎?讓我們繼續(xù)來看剛才容錯性的例子,加入集群1跪了,那么原來落在集群1上的所有數(shù)據(jù)會直接落在集群3上面,如果說每組redis集群的配置都是一樣的,那么集群3的壓力會增大,數(shù)據(jù)分布不均勻造成數(shù)據(jù)傾斜問題。
怎么搞呢?
歪果仁的腦子就是好使,給的解決方案就是加一層虛擬層,假如每組集群都分配了2個(gè)虛擬節(jié)點(diǎn)
集群 | 虛擬節(jié)點(diǎn) |
---|---|
集群1 | vnode1, vnode2 |
集群2 | vnode3, vnode4 |
集群3 | vnode5, vnode6 |
接下來就是把虛擬節(jié)點(diǎn)放入到一致性hash環(huán)上,在緩存數(shù)據(jù)的時(shí)候根據(jù)順時(shí)針查找虛擬節(jié)點(diǎn),在根據(jù)虛擬節(jié)點(diǎn)的和實(shí)際集群的對應(yīng)關(guān)系把數(shù)據(jù)存放到redis集群,這樣數(shù)據(jù)就會均勻的分布到各組集群中。
這時(shí)候如果有一組redis集群出現(xiàn)了問題,那么這組集群上面的key會相對均勻的分?jǐn)偟狡渌荷稀?/p>
從上面的結(jié)果來看,只要每組集群對應(yīng)的虛擬節(jié)點(diǎn)越多,那么各個(gè)物理集群的數(shù)據(jù)分布越均勻,當(dāng)新增加或者減少物理集群影響也會最小,但是如果虛擬節(jié)點(diǎn)太多會影響查找的性能,太少數(shù)據(jù)又會不均勻,那么多少合適呢?根據(jù)一些大神的經(jīng)驗(yàn)給出的建議是 150 個(gè)虛擬節(jié)點(diǎn)。
實(shí)現(xiàn)思路:在每次添加物理節(jié)點(diǎn)的時(shí)候,根據(jù)物理節(jié)點(diǎn)的名字生成虛擬節(jié)點(diǎn)的名字,把虛擬節(jié)點(diǎn)的名字求hash值,然后把hash值作為key,物理節(jié)點(diǎn)作為value存放到Map中;這里我們選擇使用TreeMap,因?yàn)樾枰猭ey是順序的存儲;在計(jì)算數(shù)據(jù)key需要存放到哪個(gè)物理節(jié)點(diǎn)時(shí),先計(jì)算出key的hash值,然后調(diào)用TreeMap.tailMap()返回比hash值大的map子集,如果子集為空就需要把TreeMap的第一個(gè)元素返回,如果不為空,那么取子集中的第一個(gè)元素。
> 不扯廢話,直接上代碼,No BB . Show me the code
測試刪除節(jié)點(diǎn)node3,對比命中率影響了多少 添加如下代碼:
執(zhí)行結(jié)果:
測試添加節(jié)點(diǎn)node5,對比命中率影響了多少 添加如下代碼:
執(zhí)行結(jié)果:
看上圖,在Nginx請求的分發(fā)過程中,為了讓應(yīng)用本地的緩存命中率最高,我們希望根據(jù)請求的URL或者URL參數(shù)將相同的請求轉(zhuǎn)發(fā)到同一個(gè)應(yīng)用服務(wù)器中,這個(gè)時(shí)候也可以選擇使用一致性hash算法。具體配置可以參考官方文檔: https://www.nginx.com/resources/wiki/modules/consistent_hash/
“如何用Java實(shí)現(xiàn)一致性Hash算法”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。