一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

一致性哈希算法簡介

算法與數(shù)據(jù)結(jié)構(gòu) ? 來源:labuladong ? 2023-04-04 11:53 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

最近有一位讀者跟我交流,說除了算法題之外,系統(tǒng)設(shè)計題是一大痛點。算法題起碼有很多刷題平臺可以動手實踐,但系統(tǒng)設(shè)計類的題目一般很難實踐,所以看一些教程總結(jié)也只是一知半解,遇到讓寫代碼實現(xiàn)系統(tǒng)的就懵了。

比如他最近被問到一個大型爬蟲系統(tǒng)的設(shè)計題,讓手寫一致性哈希算法,加上一系列 follow up,就被難住了。

說實話這個算法的實現(xiàn)并不難,所以本文就結(jié)合一致性哈希算法在工程中的應(yīng)用場景介紹一下這個算法算法,并給出代碼實現(xiàn),避免大家重蹈覆轍。

一致性哈希算法簡介

這個名詞大家肯定不陌生,應(yīng)該也大概理解這個算法的邏輯,不過我這里還是要再介紹一下。

一致性哈希主要解決把數(shù)據(jù)平均分配到多個節(jié)點上的問題,并且某些節(jié)點上線/下線后依然能夠做到自動負(fù)載均衡。

其原理就是抽象出一個哈希環(huán),把服務(wù)器節(jié)點的 id 通過哈希函數(shù)映射到這個哈希環(huán)上:

ca0e0d10-d298-11ed-bfe3-dac502259ad0.jpg

同時,把需要處理的數(shù)據(jù)也通過哈希函數(shù)映射到這個哈希環(huán)上,然后順時針找,遇到的第一個服務(wù)器節(jié)點來負(fù)責(zé)處理這個數(shù)據(jù):

ca202ab8-d298-11ed-bfe3-dac502259ad0.jpg

這樣一來,我們其實已經(jīng)提供了一種機制將若干數(shù)據(jù)分布在若干服務(wù)節(jié)點上了,不妨稱它為 V1 版本的一致性哈希算法。

但 V1 版本的算法還有問題:負(fù)載分布很可能不均衡。由于哈希函數(shù)的結(jié)果是難以預(yù)測的,所以可能出現(xiàn)這種情況:

ca2baf28-d298-11ed-bfe3-dac502259ad0.jpg

即某些服務(wù)器節(jié)點要負(fù)責(zé)的哈希環(huán)很長,而其他服務(wù)器負(fù)責(zé)的哈希環(huán)很短。這就會導(dǎo)致某些服務(wù)器負(fù)載很高,而其他的服務(wù)器負(fù)載很低,很不均衡。

而且,當(dāng)某個服務(wù)器節(jié)點下線后,它的負(fù)載會順時針轉(zhuǎn)移到下一個節(jié)點上,那么某些特定的節(jié)點下線順序很可能導(dǎo)致某些服務(wù)器節(jié)點負(fù)責(zé)的哈希環(huán)不斷加長,負(fù)載不斷增加。專業(yè)點說,這就是「數(shù)據(jù)傾斜」。

如何解決數(shù)據(jù)傾斜的問題呢?可以給每個服務(wù)器節(jié)點添加若干「虛擬節(jié)點」分布在哈希環(huán)上,我們不妨稱其為 V2 版的一致性哈希算法:

ca39e728-d298-11ed-bfe3-dac502259ad0.jpg

上圖給每個 Node 設(shè)置了 4 個虛擬節(jié)點,這樣一來,由于哈希函數(shù)的隨機性,每個服務(wù)器節(jié)點的虛擬節(jié)點能夠平均分布在哈希環(huán)上,那么數(shù)據(jù)就能夠比較均勻地分配到所有服務(wù)器節(jié)點上。

如果某個服務(wù)器節(jié)點下線,那么該服務(wù)器節(jié)點的所有虛擬節(jié)點都會從哈希環(huán)上摘除,它們的負(fù)載會遷移到順時針的下一個服務(wù)器節(jié)點上。

和 V1 版算法不同的是,因為虛擬節(jié)點有多個,它們的下一位不太可能是同一臺服務(wù)器的虛擬節(jié)點,所以它們的負(fù)載大概率會均分到多臺服務(wù)器的虛擬節(jié)點上。

綜上,V2 版算法通過虛擬節(jié)點的方式完美解決了數(shù)據(jù)傾斜的問題,是不是很巧妙?不過俗話說,紙上得來終覺淺,絕知此事要躬行,我們需要實踐才能真正寫出正確的一致性哈希算法。

比方說,應(yīng)該用什么數(shù)據(jù)結(jié)構(gòu)實現(xiàn)哈希環(huán)?如果哈希函數(shù)出現(xiàn)哈希沖突怎么辦?真正寫代碼的時候,這些細(xì)節(jié)問題都是要考慮的。

下面我們就結(jié)合代碼和實際場景來看看一致性哈希算法的真實應(yīng)用

實際場景分析

就以消息隊列的消費模型為例吧,我在前文用消息隊列做一個聯(lián)機游戲介紹過 Apache Pulsar 的消費模型,Pulsar 通過 subscription 的抽象提供多種訂閱模式,其中 key_shared 模式比較有意思:

每條消息會有一個 key,Pulsar 可以根據(jù)這個 key 分發(fā)消息,保證帶有相同 key 的消息分發(fā)到同一個消費者上。

官網(wǎng)的這幅圖比較好理解,圖中的K就是指消息的 key,V就是指消息的數(shù)據(jù):

ca532dfa-d298-11ed-bfe3-dac502259ad0.png

通過某些算法,所有的消息都會有消費者去處理,比如上圖就是consumerA負(fù)責(zé)處理key=K3的消息,consumerB負(fù)責(zé)處理key=K1的消息,consumerC負(fù)責(zé)處理key=K2的消息。

當(dāng)然,如果有 consumer 加入或者離開,消息的分配會重置。比如consumerA下線,那么key=K3的消息會被分配給其他消費者消費;如果有新的消費者consumerD加入,那么當(dāng)前的分配方案也可能會改變。

簡單總結(jié)就是:

1、在沒有 consumer 加入或者離開的前提下,保證 key 相同的消息都會分配到固定的 consumer,不能一會兒分配到consumerA,一會兒分配給consumerB。

2、如果有 consumer 加入或者離開,可以重新進行分配每個 consumer 負(fù)責(zé)的 key,要求盡量把 key 平均分配給 consumer,避免出現(xiàn)某些 consumer 負(fù)責(zé)過多 key 的情況導(dǎo)致數(shù)據(jù)傾斜。

3、以上兩個操作,尤其是給 consumer 重新分配 key 的操作,效率要盡可能高。

對于上述場景,你如何設(shè)計分配算法,把這些帶有 key 的消息高效地、均勻地分配給所有 consumer 呢

我們來看看 Pulsar 是如何做的,官網(wǎng)對這部分的實現(xiàn)原理描述的比較清楚,參考鏈接如下:

https://pulsar.apache.org/docs/next/concepts-messaging/#key_shared

結(jié)合我之前在學(xué)習(xí)開源項目的套路中介紹的查看源碼背景信息的技巧,可以發(fā)現(xiàn) Pulsar 的 key_shared 模式的消費者實現(xiàn)其實是經(jīng)歷了一些演進的。

最開始的默認(rèn)實現(xiàn)方式叫做 Auto-split Hash Range,即抽象出來一個[0, 65535]的哈希區(qū)間,讓每個 consumer 負(fù)責(zé)這個區(qū)間的一部分。比如有C1~C44 個 consumer,那么它們會平分整個哈希區(qū)間:

016,38432,76849,15265,536
|-------C3------|-------C2------|-------C4------|-------C1------|

然后我們可以對每條消息的 key 計算哈希值并求模映射到[0, 65535]的區(qū)間中,這樣我們就可以選出負(fù)責(zé)處理這條消息的 consumer 了,而且 key 相同的消息總會分配到這個 consumer 上。

那么如果有 consumer 上線或者下線怎么處理呢?

如果有 consumer 下線,那么它負(fù)責(zé)的哈希區(qū)間會直接交給右側(cè)的 consumer。比如上例中C4下線,那么哈希區(qū)間就會變成這樣:

016,38432,76865,536
|-------C3------|-------C2------|----------------C1---------------|

當(dāng)然這里也有個特殊情況,就是下線的那個 consumer 右邊沒有其他 consumer 的情況,我們可以讓它左邊的 consumer 頂上來。比如現(xiàn)在的C1下線,那么就讓左邊的C2負(fù)責(zé)C1的區(qū)間:

016,38465,536
|-------C3------|--------------------------C2-----------------------|

如果有 consumer 上線,那么算法可以把最長的哈希區(qū)間平分,分一半給新來的 consumer。比如此時C5上線,我們就可以把C2負(fù)責(zé)的一半哈希區(qū)間分給C5:

016,38440,96065,536
|-------C3------|-----------C5-----------|----------C2----------|

這就是 Auto-split Hash Range 的方案,不算復(fù)雜,具體的實現(xiàn)可以看 Pulsar 源碼中HashRangeAutoSplitStickyKeyConsumerSelector這個類,我在這里就不列舉了。

這個方案的問題主要還是數(shù)據(jù)傾斜,比如上面的例子出現(xiàn)的這種情況,C2的負(fù)載顯然比C3多很多:

016,38465,536
|-------C3------|--------------------------C2-----------------------|

按照這個算法邏輯,一些 consumer 下線后很容易產(chǎn)生這種數(shù)據(jù)傾斜的情況,所以這個解決方案并不能均勻地把 key 分配給 consumer

那么如何優(yōu)化這個算法呢?就要用到一致性哈希算法了。

一致性哈希算法的實現(xiàn)

結(jié)合我在本文開頭對一致性哈希算法的介紹,應(yīng)該很容易想到優(yōu)化思路。其實現(xiàn)在 Pulsar 就是使用一致性哈希算法來實現(xiàn)的 key_shared 訂閱。

首先抽象出一個值在[0, MAX_INT]的哈希環(huán),然后給每個 consumer 分配 100 個虛擬節(jié)點映射到這個哈希環(huán)上。接下來,我們把 key 的哈希值放在哈希環(huán)上,順時針方向找到最近的 consumer 虛擬節(jié)點,也就找到了負(fù)責(zé)處理這個 key 的 consumer。

哈希環(huán)我們一般用 TreeMap 實現(xiàn),直接看 Pulsar 源碼中ConsistentHashingStickyKeyConsumerSelector的實現(xiàn)吧,我提取了其中的關(guān)鍵邏輯并添加了詳細(xì)的注釋:

classConsistentHashingStickyKeyConsumerSelector{
//哈希環(huán),虛擬節(jié)點的哈希值->consumer列表
//因為存在哈希沖突,多個虛擬節(jié)點可能映射到同一個哈希值,所以值為List類型
NavigableMap>hashRing=newTreeMap<>();
//每個consumer有100個虛擬節(jié)點
intnumberOfPoints=100;

//將該consumer的100個虛擬節(jié)點添加到哈希環(huán)上
publicvoidaddConsumer(Consumerconsumer){
for(inti=0;i());
hashRing.get(hash).add(consumer);
}
}

//在哈希環(huán)上刪除該consumer的所有虛擬節(jié)點
publicvoidremoveConsumer(Consumerconsumer){
for(inti=0;i>ceilingEntry=hashRing.ceilingEntry(hash);
ListconsumerList;
if(ceilingEntry!=null){
consumerList=ceilingEntry.getValue();
}else{
//哈希環(huán)順時針轉(zhuǎn)一圈,回到開頭尋找第一個節(jié)點
consumerList=hashRing.firstEntry().getValue();
}
//保證相同的key都會分配到同一個consumer
returnconsumerList.get(hash%consumerList.size());
}
}

當(dāng)消息被發(fā)送過來后,Pulsar 可以通過select方法選擇對應(yīng)的 consumer 來處理數(shù)據(jù);當(dāng)新的 consumer 上線時,可以通過addConsumer將它的虛擬節(jié)點放到哈希環(huán)上并開始接收消息;當(dāng)有 consumer 下線時,可以通過removeConsumer將它的虛擬節(jié)點從哈希環(huán)上摘除,由其他 consumer 承擔(dān)它的工作。

因為每個 consumer 有 100 個虛擬節(jié)點,所以在 consumer 下線時,負(fù)載其實是均勻地分配給了其他 consumer,因此一致性哈希算法能夠解決之前 Auto-split Hash Range 方案數(shù)據(jù)傾斜的問題。




審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 虛擬機
    +關(guān)注

    關(guān)注

    1

    文章

    966

    瀏覽量

    29368
  • 哈希算法
    +關(guān)注

    關(guān)注

    1

    文章

    56

    瀏覽量

    10965

原文標(biāo)題:一致性哈希算法設(shè)計題,栽了

文章出處:【微信號:TheAlgorithm,微信公眾號:算法與數(shù)據(jù)結(jié)構(gòu)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    順序一致性和TSO一致性分別是什么?SC和TSO到底哪個好?

    內(nèi)存一致性之順序一致性(sequential consistency)可以說,最直觀的內(nèi)存一致性模型是sequentially consistent(SC):內(nèi)存訪問執(zhí)行的順序與程序指定的順序相同
    發(fā)表于 07-19 14:54

    一致性規(guī)劃研究

    針對一致性規(guī)劃的高度求解復(fù)雜度,分析主流一致性規(guī)劃器的求解策略,給出影響一致性規(guī)劃器性能的主要因素:啟發(fā)信息的有效,信念狀態(tài)表示方法的緊湊
    發(fā)表于 04-06 08:43 ?12次下載

    CMP中Cache一致性協(xié)議的驗證

    CMP是處理器體系結(jié)構(gòu)發(fā)展的個重要方向,其中Cache一致性問題的驗證是CMP設(shè)計中的項重要課題?;贛ESI一致性協(xié)議,本文建立了CMP的Cache
    發(fā)表于 07-20 14:18 ?38次下載

    加速器一致性接口

    Zynq PS上的加速器一致性接口(Accelerator Coherency Port, ACP)是個兼容AXI3的64位從機接口,連接到SCU(Snoop Control Unit),為PL
    發(fā)表于 11-17 15:04 ?4038次閱讀

    分布式一致性算法Yac

    傳統(tǒng)靜態(tài)拓?fù)渲鲝哪P头植际?b class='flag-5'>一致性算法存在嚴(yán)重負(fù)載不均及單點性能瓶頸效應(yīng),且崩潰節(jié)點大于集群規(guī)模的50qo時算法無法正常工作。針對上述問題,提出基于動態(tài)拓?fù)浼坝邢薇頉Q思想的分布式一致性
    發(fā)表于 11-27 17:49 ?0次下載
    分布式<b class='flag-5'>一致性</b><b class='flag-5'>算法</b>Yac

    基于軌跡標(biāo)簽的謠言一致性維護算法

    針對發(fā)布/訂閱系統(tǒng)中緩存副本一致性維護問題,首先,對原有基于謠言的一致性維護算法進行改進,提出種基于軌跡標(biāo)簽的謠言一致性維護
    發(fā)表于 12-17 11:35 ?0次下載
    基于軌跡標(biāo)簽的謠言<b class='flag-5'>一致性</b>維護<b class='flag-5'>算法</b>

    Cache一致性協(xié)議優(yōu)化研究

    問題的由來.總結(jié)了多核時代高速緩存一致性協(xié)議設(shè)計的關(guān)鍵問題,綜述了近年來學(xué)術(shù)界對一致性的研究.從程序訪存行為模式、目錄組織結(jié)構(gòu)、一致性粒度、一致性協(xié)議流量、目錄協(xié)議的可擴展性等方面,闡
    發(fā)表于 12-30 15:04 ?0次下載
    Cache<b class='flag-5'>一致性</b>協(xié)議優(yōu)化研究

    優(yōu)化模型的乘偏好關(guān)系一致性改進

    針對乘偏好信息下的決策問題,引入乘偏好關(guān)系的有序一致性、滿意一致性以及一致性指數(shù)等概念,建立以偏差變量最小化為目標(biāo)函數(shù)的優(yōu)化模型,進而構(gòu)
    發(fā)表于 03-20 17:28 ?0次下載

    一致性哈希是什么?為什么它是可擴展的分布式系統(tǒng)架構(gòu)的個必要工具

    在本文中,我們將了解一致性哈希是什么、為什么它是可擴展的分布式系統(tǒng)架構(gòu)中的個必要工具。
    的頭像 發(fā)表于 07-17 17:57 ?4629次閱讀

    哈希圖一致性算法已被驗證為異步拜占庭容錯

    HederaHashgraph在下代公共分類帳中擁有多樣化的治理。它最近宣布哈希圖一致性算法已被驗證為異步拜占庭容錯。這是通過使用Coq系統(tǒng)的計算機檢查的數(shù)學(xué)證明完成的。
    發(fā)表于 10-23 11:07 ?1959次閱讀

    基于改進一致性的多無人機編隊控制算法

    基于改進一致性的多無人機編隊控制算法
    發(fā)表于 06-22 16:02 ?16次下載

    Dubbo負(fù)載均衡策略之一致性哈希

    本文主要講解了一致性哈希算法的原理以及其存在的數(shù)據(jù)傾斜的問題,然后引出解決數(shù)據(jù)傾斜問題的方法,最后分析一致性哈希
    的頭像 發(fā)表于 06-16 15:30 ?1289次閱讀
    Dubbo負(fù)載均衡策略之<b class='flag-5'>一致性</b><b class='flag-5'>哈希</b>

    如何保證緩存一致性

    “ 本文的參考文章是2022年HOT 34上Intel Rob Blakenship關(guān)于CXL緩存一致性篇介紹。”
    的頭像 發(fā)表于 10-19 17:42 ?1682次閱讀
    如何保證緩存<b class='flag-5'>一致性</b>

    DDR一致性測試的操作步驟

    DDR一致性測試的操作步驟? DDR(雙數(shù)據(jù)率)一致性測試是對DDR內(nèi)存模塊進行測試以確保其性能和可靠。在進行DDR一致性測試時,需要遵循
    的頭像 發(fā)表于 02-01 16:24 ?2669次閱讀

    深入理解數(shù)據(jù)備份的關(guān)鍵原則:應(yīng)用一致性與崩潰一致性的區(qū)別

    深入理解數(shù)據(jù)備份的關(guān)鍵原則:應(yīng)用一致性與崩潰一致性的區(qū)別 在數(shù)字化時代,數(shù)據(jù)備份成為了企業(yè)信息安全的核心環(huán)節(jié)。但在備份過程中,兩個關(guān)鍵概念——應(yīng)用一致性和崩潰一致性,常常被誤解或混淆。
    的頭像 發(fā)表于 03-11 11:29 ?1432次閱讀
    深入理解數(shù)據(jù)備份的關(guān)鍵原則:應(yīng)用<b class='flag-5'>一致性</b>與崩潰<b class='flag-5'>一致性</b>的區(qū)別