溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么使用java分布式系統(tǒng)中一致性哈希算法

發(fā)布時(shí)間:2021-11-17 09:22:52 來源:億速云 閱讀:124 作者:iii 欄目:大數(shù)據(jù)

本篇內(nèi)容主要講解“怎么使用java分布式系統(tǒng)中一致性哈希算法”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“怎么使用java分布式系統(tǒng)中一致性哈希算法”吧!

業(yè)務(wù)場景

近年來,由于互聯(lián)網(wǎng)的興起,B2C、O2O等商業(yè)概念的提出和移動(dòng)端的發(fā)展,使得分布式系統(tǒng)流行起來。分布式系統(tǒng)相對于單一系統(tǒng)而言,帶來了流量大、系統(tǒng)高可用和高容錯(cuò)的便利。功能強(qiáng)大的同時(shí),也意味著實(shí)現(xiàn)起來需要更多的技術(shù)支持。例如系統(tǒng)訪問層的負(fù)債均衡、緩存層的多實(shí)例主從復(fù)制備份以及數(shù)據(jù)層的分庫分表等。

我們以負(fù)載均衡為例,常見的負(fù)載均衡方法有很多,但是它們的優(yōu)缺點(diǎn)都很明顯:

  • 隨機(jī)訪問策略:系統(tǒng)隨機(jī)訪問,缺點(diǎn):可能造成服務(wù)器負(fù)載壓力不均衡,俗話講就是撐的撐死,餓的餓死。

  • 輪詢策略:請求均勻分配,如果服務(wù)器有性能差異,則無法實(shí)現(xiàn)性能好的服務(wù)器能夠多承擔(dān)一部分。

  • 權(quán)重輪詢策略:權(quán)值需要靜態(tài)配置,無法自動(dòng)調(diào)節(jié),不適合對長連接和命中率有要求的場景。

  • Hash取模策略:不穩(wěn)定,如果列表中某臺(tái)服務(wù)器宕機(jī),則會(huì)導(dǎo)致路由算法產(chǎn)生變化,由此導(dǎo)致命中率的急劇下降。

  • 一致性哈希策略(本文重點(diǎn)分析)

以上幾個(gè)策略,排除本篇介紹的一致性哈希策略,可能使用最多的就是 Hash取模策略了。Hash取模策略的缺點(diǎn)也是很明顯的,這種缺點(diǎn)也許在負(fù)載均衡的時(shí)候不是很明顯,但是在涉及數(shù)據(jù)緩存的主從備份和數(shù)據(jù)庫分庫分表中就體現(xiàn)的較為明顯了。

使用Hash取模的問題

負(fù)載均衡

負(fù)載均衡時(shí),假設(shè)現(xiàn)有3臺(tái)服務(wù)器(編號(hào)分別為0、1、2),使用哈希取模的計(jì)算方式則是:對訪問者的IP,通過固定算式hash(IP) % N(N為服務(wù)器的個(gè)數(shù)),使得每個(gè)IP都可以定位到特定的服務(wù)器。

例如現(xiàn)有IP地址 10.58.34.31,對IP哈希取模策時(shí),計(jì)算結(jié)果為2,即訪問編號(hào)為2的服務(wù)器:

String ip = "10.58.34.31";
int v1 = hash(ip) % 3;
System.out.println("訪問服務(wù)器:" + v1);// 訪問服務(wù)器:2

如果此時(shí)服務(wù)器2宕機(jī)了,則會(huì)導(dǎo)致所有計(jì)算結(jié)果為2的 IP 對應(yīng)的用戶都訪問異常(包括上例中的IP)?;蛘吣阈略隽艘慌_(tái)服務(wù)器3,這時(shí)不修改N值的話那么服務(wù)器3永遠(yuǎn)不會(huì)被訪問到。

怎么使用java分布式系統(tǒng)中一致性哈希算法

當(dāng)然如果你能動(dòng)態(tài)獲取到當(dāng)前可用服務(wù)器的個(gè)數(shù),亦即N值是根據(jù)當(dāng)前可用服務(wù)器個(gè)數(shù)動(dòng)態(tài)來變化的,則可解決此問題。但是對于特定地區(qū)或特定IP訪問特定服務(wù)器類的需求會(huì)造成訪問偏差。

分庫分表(分布式存儲(chǔ))

負(fù)載均衡中有這種問題,那么分庫分表中同樣也有這樣的問題。例如隨著業(yè)務(wù)的飛速增長,我們的注冊用戶也越來越多,單個(gè)用戶表數(shù)量已經(jīng)達(dá)到千萬級甚至更大。由于Mysql的單表建議百萬級數(shù)據(jù)存儲(chǔ),所以這時(shí)為了保證系統(tǒng)查詢和運(yùn)行效率,肯定會(huì)考慮到分庫分表。

對于分庫分表,數(shù)據(jù)的分配是個(gè)重要的問題,你需要保證數(shù)據(jù)分配在這個(gè)服務(wù)器,那么在查詢時(shí)也需要到該服務(wù)器上來查詢,否則會(huì)造成數(shù)據(jù)查詢丟失的問題。

通常是根據(jù)用戶的 ID 哈希取模得到的值然后路由到對應(yīng)的存儲(chǔ)位置,計(jì)算公式為:hash(userId) % N,其中N為分庫或分表的個(gè)數(shù)。

例如分庫數(shù)為2時(shí),計(jì)算結(jié)果為1,則ID為1010的用戶存儲(chǔ)在編號(hào)為1對應(yīng)的庫中:

String userId = "1010";
int v1 = hash(userId) % 2;
System.out.println("存儲(chǔ):" + v1);// 存儲(chǔ):1

怎么使用java分布式系統(tǒng)中一致性哈希算法

之后業(yè)務(wù)數(shù)量持續(xù)增長,又新增一臺(tái)用戶服務(wù)庫,當(dāng)我們根據(jù)ID=1010去查詢數(shù)據(jù)時(shí),路由計(jì)算方式為:

int v2 = hash(userId) % 3;
System.out.println("存儲(chǔ):" + v2);// 存儲(chǔ):0

我們得到的路由值是0,最后的結(jié)果就不用說了,存在編號(hào)1上的數(shù)據(jù)我們?nèi)ゾ幪?hào)為0的庫上去查詢肯定是得不到查詢結(jié)果的。

怎么使用java分布式系統(tǒng)中一致性哈希算法

為了數(shù)據(jù)可用,你需要做數(shù)據(jù)遷移,按照新的路由規(guī)則對所有用戶重新分配存儲(chǔ)地址。每次的庫或表的數(shù)量改變你都需要做一次全部用戶信息數(shù)據(jù)的遷移。不用想這其中的工作量是有多費(fèi)時(shí)費(fèi)力了。

是否有某種方法,有效解決這種分布式存儲(chǔ)結(jié)構(gòu)下動(dòng)態(tài)增加或刪除節(jié)點(diǎn)所帶來的問題,能保證這種不受實(shí)例數(shù)量變化影響而準(zhǔn)確路由到正確的實(shí)例上的算法或?qū)崿F(xiàn)機(jī)制呢?解決這些問題,一致性哈希算法誕生了。

一致性哈希算法

基本思想原理

一致性哈希算法是在1997年由麻省理工學(xué)院的Karger等人在解決分布式Cache中提出的,設(shè)計(jì)目標(biāo)是為了解決因特網(wǎng)中的熱點(diǎn)(Hot spot)問題,初衷和CARP十分類似。一致性哈希修正了CARP使用的簡單哈希算法帶來的問題,使得DHT可以在P2P環(huán)境中真正得到應(yīng)用。

上面說的哈希取模方法,它是針對一個(gè)點(diǎn)的,業(yè)務(wù)布局嚴(yán)重依賴于這個(gè)計(jì)算的點(diǎn)值結(jié)果。你結(jié)算的結(jié)果是2,那么就對應(yīng)到編號(hào)為2的服務(wù)器上。這樣的映射就造成了業(yè)務(wù)容錯(cuò)性和可擴(kuò)展性極低。

我們思考下,是否可以將這個(gè)計(jì)算結(jié)果的點(diǎn)值賦予范圍的意義?我們知道Hash取模之后得到的是一個(gè) int 型的整值。

// Objects 類中默認(rèn)的 hash 方法
public static int hash(Object... values) {
    return Arrays.hashCode(values);
}

既然 hash的計(jì)算結(jié)果是 int 類型,而 java 中 int 的最小值是-2^31,最大值是2^31-1。意味著任何通過哈希取模之后的無符號(hào)值都會(huì)在 0 ~ 2^31-1范圍之間,共2^32個(gè)數(shù)。那我們是否可以不對服務(wù)器的數(shù)量進(jìn)行取模而是直接對2^32取模。這就形成了一致性哈希的基本算法思想,什么意思呢?

這里需要注意一點(diǎn): 默認(rèn)的 hash 方法結(jié)果是有負(fù)值的情況,因此需要我們重寫hash方法,保證哈希值的非負(fù)性。

簡單來說,一致性Hash算法將整個(gè)哈希值空間組織成一個(gè)虛擬的圓環(huán),如假設(shè)某哈希函數(shù) H 的值空間為 0 ~ 2^32-1(即哈希值是一個(gè)32位無符號(hào)整形),整個(gè)哈希環(huán)如下:

整個(gè)空間圓按順時(shí)針方向布局,圓環(huán)的正上方的點(diǎn)代表0,0點(diǎn)右側(cè)的第一個(gè)點(diǎn)代表1。以此類推2、3、4、5、6……直到2^32-1,也就是說0點(diǎn)左側(cè)的第一個(gè)點(diǎn)代表2^32-1, 0和2^32-1在零點(diǎn)中方向重合,我們把這個(gè)由2^32個(gè)點(diǎn)組成的圓環(huán)稱為 環(huán)形Hash空間

那么,一致性哈希算法與上圖中的圓環(huán)有什么關(guān)系呢?仍然以之前描述的場景為例,假設(shè)我們有4臺(tái)服務(wù)器,服務(wù)器0、服務(wù)器1、服務(wù)器2,服務(wù)器3,那么,在生產(chǎn)環(huán)境中,這4臺(tái)服務(wù)器肯定有自己的 IP 地址或主機(jī)名,我們使用它們各自的 IP 地址或主機(jī)名作為關(guān)鍵字進(jìn)行哈希計(jì)算,使用哈希后的結(jié)果對2^32取模,可以使用如下公式示意:

 hash(服務(wù)器的IP地址) %  2^32

最后會(huì)得到一個(gè) [0, 2^32-1]之間的一個(gè)無符號(hào)整形數(shù),這個(gè)整數(shù)就代表服務(wù)器的編號(hào)。同時(shí)這個(gè)整數(shù)肯定處于[0, 2^32-1]之間,那么,上圖中的 hash 環(huán)上必定有一個(gè)點(diǎn)與這個(gè)整數(shù)對應(yīng)。那么這個(gè)服務(wù)器就可以映射到這個(gè)環(huán)上。

多個(gè)服務(wù)器都通過這種方式進(jìn)行計(jì)算,最后都會(huì)各自映射到圓環(huán)上的某個(gè)點(diǎn),這樣每臺(tái)機(jī)器就能確定其在哈希環(huán)上的位置,如下圖所示:

怎么使用java分布式系統(tǒng)中一致性哈希算法

如何提高容錯(cuò)性和擴(kuò)展性的

那么用戶訪問,如何分配訪問的服務(wù)器呢?我們根據(jù)用戶的 IP 使用上面相同的函數(shù) Hash 計(jì)算出哈希值,并確定此數(shù)據(jù)在環(huán)上的位置,從此位置沿環(huán) 順時(shí)針行走,遇到的第一臺(tái)服務(wù)器就是其應(yīng)該定位到的服務(wù)器。

怎么使用java分布式系統(tǒng)中一致性哈希算法

從上圖可以看出 用戶1 順時(shí)針遇到的第一臺(tái)服務(wù)器是 服務(wù)器3 ,所以該用戶被分配給服務(wù)器3來提供服務(wù)。同理可以看出用戶2被分配給了服務(wù)器2。

新增服務(wù)器節(jié)點(diǎn)

如果這時(shí)需要新增一臺(tái)服務(wù)器節(jié)點(diǎn),一致性哈希策略是如何應(yīng)對的呢?如下圖所示,我們新增了一臺(tái)服務(wù)器4,通過上述一致性哈希算法計(jì)算后得出它在哈希環(huán)的位置。

怎么使用java分布式系統(tǒng)中一致性哈希算法

可以發(fā)現(xiàn),原來訪問服務(wù)器3的用戶1現(xiàn)在訪問的對象是服務(wù)器4,用戶能正常訪問且服務(wù)不需要停機(jī)就可以自動(dòng)切換。

刪除服務(wù)器節(jié)點(diǎn)

如果這時(shí)某臺(tái)服務(wù)器異常宕機(jī)或者運(yùn)維撤銷了一臺(tái)服務(wù)器,那么這時(shí)會(huì)發(fā)生什么情況呢?如下圖所示,假設(shè)我們撤銷了服務(wù)器2。

怎么使用java分布式系統(tǒng)中一致性哈希算法

可以看出,我們服務(wù)仍然能正常提供服務(wù),只不過這時(shí)用戶2會(huì)被分配到服務(wù)1上了而已。

通過一致性哈希的方式,我們提高了我們系統(tǒng)的容錯(cuò)性和可擴(kuò)展性,分布式節(jié)點(diǎn)的變動(dòng)不會(huì)影響整個(gè)系統(tǒng)的運(yùn)行且不需要我們做一些人為的調(diào)整策略。

Hash 環(huán)的數(shù)據(jù)傾斜性

一致性哈希雖然為我們提供了穩(wěn)定的切換策略,但是它也有一些小缺陷。因?yàn)?hash取模算法得到的結(jié)果是隨機(jī)的,我們并不能保證各個(gè)服務(wù)節(jié)點(diǎn)能均勻的分配到哈希環(huán)上。

例如當(dāng)有4個(gè)服務(wù)節(jié)點(diǎn)時(shí),我們把哈希環(huán)認(rèn)為是一個(gè)圓盤時(shí)鐘,我們并不能保證4個(gè)服務(wù)節(jié)點(diǎn)剛好均勻的落在時(shí)鐘的 12、3、6、9點(diǎn)上。

分布不均勻就會(huì)產(chǎn)生一個(gè)問題,用戶的請求訪問就會(huì)不均勻,同時(shí)4個(gè)服務(wù)承受的壓力就會(huì)不均勻。這種問題現(xiàn)象我們稱之為,Hash環(huán)的數(shù)據(jù)傾斜問題

怎么使用java分布式系統(tǒng)中一致性哈希算法

如上圖所示,服務(wù)器0 到 服務(wù)器1 之間的哈希點(diǎn)值占據(jù)比例最大,大量請求會(huì)集中到 服務(wù)器1 上,而只有極少量會(huì)定位到 服務(wù)器0 或其他幾個(gè)節(jié)點(diǎn)上,從而出現(xiàn) hash環(huán)偏斜的情況。

如果想要均衡的將緩存分布到每臺(tái)服務(wù)器上,最好能讓這每臺(tái)服務(wù)器盡量多的、均勻的出現(xiàn)在hash環(huán)上,但是如上圖中所示,真實(shí)的服務(wù)器資源只有4臺(tái),我們怎樣憑空的讓它們多起來呢?

既然沒有多余的真正的物理服務(wù)器節(jié)點(diǎn),我們就只能將現(xiàn)有的物理節(jié)點(diǎn)通過虛擬的方法復(fù)制出來。

這些<u>由實(shí)際節(jié)點(diǎn)虛擬復(fù)制而來的節(jié)點(diǎn)</u>被稱為 "虛擬節(jié)點(diǎn)",即對每一個(gè)服務(wù)節(jié)點(diǎn)計(jì)算多個(gè)哈希,每個(gè)計(jì)算結(jié)果位置都放置一個(gè)此服務(wù)節(jié)點(diǎn),稱為虛擬節(jié)點(diǎn)。

怎么使用java分布式系統(tǒng)中一致性哈希算法

假如服務(wù)器1 的 IP 是 192.168.32.132,那么原服務(wù)器1 節(jié)點(diǎn)在環(huán)形空間的位置就是hash("192.168.32.132") % 2^32

我們基于 服務(wù)器1 構(gòu)建兩個(gè)虛擬節(jié)點(diǎn),Server1-A 和 Server1-B,虛擬節(jié)點(diǎn)在環(huán)形空間的位置可以利用(IP+后綴)計(jì)算,例如:

hash("192.168.32.132#A") % 2^32
hash("192.168.32.132#B") % 2^32

此時(shí),環(huán)形空間中不再有物理節(jié)點(diǎn) 服務(wù)器1,服務(wù)器2,……,替代的是只有虛擬節(jié)點(diǎn) Server1-A,Server1-B,Server2-A,Server2-B,……。

怎么使用java分布式系統(tǒng)中一致性哈希算法

同時(shí)數(shù)據(jù)定位算法不變,只是多了一步虛擬節(jié)點(diǎn)到實(shí)際節(jié)點(diǎn)的映射,例如定位到 “Server1-A”、“Server1-B” 兩個(gè)虛擬節(jié)點(diǎn)的數(shù)據(jù)均定位到 服務(wù)器1上。這樣就解決了服務(wù)節(jié)點(diǎn)少時(shí)數(shù)據(jù)傾斜的問題。

在實(shí)際應(yīng)用中,通常將虛擬節(jié)點(diǎn)數(shù)設(shè)置為32甚至更大,因此即使很少的服務(wù)節(jié)點(diǎn)也能做到相對均勻的數(shù)據(jù)分布。由于虛擬節(jié)點(diǎn)數(shù)量較多,與虛擬節(jié)點(diǎn)的映射關(guān)系也變得相對均衡了。

我們再來看下 一致性哈希算法的命中率計(jì)算公式:(1-n/(n+m))*100%,n 代表服務(wù)器臺(tái)數(shù),m 代表變動(dòng)服務(wù)器臺(tái)數(shù),當(dāng)服務(wù)器樣本臺(tái)數(shù)足夠大,則當(dāng)變動(dòng)服務(wù)器臺(tái)數(shù)越大時(shí),命中率會(huì)越小,影響會(huì)越來越小。

到此,相信大家對“怎么使用java分布式系統(tǒng)中一致性哈希算法”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(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)容。

AI