<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一致性哈希及其在Greenplum中的應(yīng)用

          共 4353字,需瀏覽 9分鐘

           ·

          2021-04-23 21:10

          點(diǎn)擊上方藍(lán)色字體,選擇“設(shè)為星標(biāo)
          回復(fù)”資源“獲取更多資源
          前言

          一致性哈希(consistent hashing)是分布式系統(tǒng)中非常重要的算法,在平滑擴(kuò)縮容、動(dòng)態(tài)負(fù)載均衡等方向有大量應(yīng)用。相對(duì)于傳統(tǒng)的線性(取模)哈希算法,一致性哈希可以保證在分布式哈希表中的桶數(shù)量發(fā)生變化時(shí),受到影響需要重新映射的key盡量少。本文先簡(jiǎn)要復(fù)習(xí)下經(jīng)典的割環(huán)一致性哈希方案,然后介紹它的變種——跳躍一致性哈希(jump consistent hash)。

          割環(huán)一致性哈希

          一致性哈希的概念最初在1997年由David Karger等大佬提出,原始論文見(jiàn)Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web,起初是為了解決網(wǎng)絡(luò)中的熱點(diǎn)問(wèn)題,后來(lái)發(fā)展成分布式系統(tǒng)中通用的算法。為了與此后出現(xiàn)的其他一致性哈希算法相區(qū)別,一般將這個(gè)經(jīng)典方法稱(chēng)為“割環(huán)法”。該算法能夠滿足論文中提出的兩大目標(biāo),即平衡性(balance)和單調(diào)性(monotonicity)。

          顧名思義,割環(huán)法將整個(gè)哈??臻g組織成一個(gè)首尾相接的圓環(huán),一般設(shè)為[0, 232 - 1]。以分布式K-V存儲(chǔ)為例,哈希桶即為存儲(chǔ)節(jié)點(diǎn)。將節(jié)點(diǎn)N的編號(hào)或IP等按哈希函數(shù)hash(N)映射在環(huán)上,再將數(shù)據(jù)的key按同樣的哈希函數(shù)hash(k)映射在環(huán)上,數(shù)據(jù)就會(huì)存儲(chǔ)在環(huán)上以順時(shí)針?lè)较虮闅v找到的第一個(gè)節(jié)點(diǎn)。當(dāng)節(jié)點(diǎn)擴(kuò)容或縮容時(shí),仍然按照順時(shí)針原則,將受到影響的區(qū)間內(nèi)的數(shù)據(jù)重新分布到相鄰的節(jié)點(diǎn)上去,達(dá)到增量更新的目的,即滿足單調(diào)性。以下3張圖能夠簡(jiǎn)單地說(shuō)明。

          雖然哈希函數(shù)的結(jié)果是均勻的,但節(jié)點(diǎn)映射在環(huán)上可能不均勻,節(jié)點(diǎn)數(shù)越少,數(shù)據(jù)傾斜的可能性就越大。解決此問(wèn)題的方法是將物理節(jié)點(diǎn)虛擬成多個(gè)影子節(jié)點(diǎn),數(shù)據(jù)經(jīng)過(guò)哈希后按順時(shí)針原則落到影子節(jié)點(diǎn)指向的物理節(jié)點(diǎn)上。如果我們想要人為干預(yù)各節(jié)點(diǎn)上數(shù)據(jù)量的權(quán)重,還可以指定不同的影子節(jié)點(diǎn)數(shù)量。如下圖所示,影子節(jié)點(diǎn)數(shù)量為3:2:2:1。

          虛擬節(jié)點(diǎn)擴(kuò)縮容時(shí)的數(shù)據(jù)遷移方法與僅采用物理節(jié)點(diǎn)相同,因此調(diào)整權(quán)重值也會(huì)觸發(fā)數(shù)據(jù)遷移。

          對(duì)于有N個(gè)桶和K個(gè)鍵的一致性哈希方案,其時(shí)間復(fù)雜度是:

          • 添加、刪除節(jié)點(diǎn)——O(K / N + logN);

          • 添加、刪除key——O(logN)。

          其中,O(K / N)是數(shù)據(jù)重分布操作的平均代價(jià),O(log N)則是在環(huán)上進(jìn)行二分查找定位哈希桶的代價(jià)。

          最后有一個(gè)小問(wèn)題:節(jié)點(diǎn)擴(kuò)縮容以及節(jié)點(diǎn)宕機(jī)時(shí)如何保證系統(tǒng)仍然可用呢?有兩種直接的思路:

          • 中繼——如果在某個(gè)節(jié)點(diǎn)上查不到所需的數(shù)據(jù),就把請(qǐng)求轉(zhuǎn)發(fā)給該節(jié)點(diǎn)的順時(shí)針?lè)较蛳乱粋€(gè)節(jié)點(diǎn)進(jìn)行處理。

          • 雙寫(xiě)——每次寫(xiě)入數(shù)據(jù)時(shí),都另外寫(xiě)一份到目標(biāo)節(jié)點(diǎn)的順時(shí)針?lè)较蛳乱粋€(gè)節(jié)點(diǎn)。

          割環(huán)法已經(jīng)能夠滿足一般分布式系統(tǒng)中的多數(shù)需求,Cassandra、Memcached等著名的存儲(chǔ)系統(tǒng)都用到了它(注意Redis Cluster并沒(méi)有)。下面介紹思想更加精妙,效率也更高的跳躍一致性哈希(jump consistent hash)方法。

          跳躍一致性哈希

          這個(gè)算法比較年輕,在2014年由Google的大佬John Lamping和Eric Veach提出,原始論文見(jiàn)A Fast, Minimal Memory, Consistent Hash Algorithm。它的實(shí)現(xiàn)非常簡(jiǎn)潔,僅有5行代碼,如下。

          int32_t JumpConsistentHash(uint64_t key, int32_t num_buckets) {
          int64_t b = 1, j = 0?
          while (j < num_buckets) {
          b = j?
          key = key * 2862933555777941757ULL + 1?
          j = (b + 1) * (double(1LL << 31) / double((key >> 33) + 1))?
          }
          return b?
          }

          看官可能還無(wú)法理解為什么能這樣實(shí)現(xiàn),接下來(lái)重走一遍論文的推導(dǎo)思路。

          假設(shè)最終要求的滿足平衡性和單調(diào)性的哈希函數(shù)是ch(k, n)(k為數(shù)據(jù)的鍵,n為哈希桶的數(shù)量),有如下簡(jiǎn)單的遞推關(guān)系:

          • 當(dāng)n = 1時(shí),所有key都要映射到同一個(gè)桶中,即ch(k, 1) = 0;

          • 當(dāng)n = 2時(shí),為保證均勻性,需有K / 2個(gè)key分別映射到兩個(gè)桶中(K是key的總數(shù)量),故K / 2個(gè)key需要重新映射;

          • ......

          • 當(dāng)桶數(shù)量由n變?yōu)閚 + 1時(shí),有K / (n + 1)個(gè)key需要重新映射。

          那么該如何決定哪些key被重新映射到新的桶中呢?答案是采用線性同余法(LCG)生成的偽隨機(jī)數(shù)決定。上文中的magic number 2862933555777941757就是線性同余法的乘數(shù)a。

          以k作為種子生成一個(gè)偽隨機(jī)數(shù)序列,可以保證對(duì)于確定的k,ch(k, n)的結(jié)果也是確定的,進(jìn)而使用條件rand < 1 / (j + 1)即可保證哈希桶由j個(gè)變?yōu)閖 + 1個(gè)時(shí),有1 / (j + 1)比例的數(shù)據(jù)會(huì)重新映射。

          此時(shí)ch()函數(shù)的邏輯如下,時(shí)間復(fù)雜度顯然為O(n)。

          int ch(int key, int num_buckets) {
          random.seed(key)?
          int b = 0? // This will track ch(key, j+1).
          for (int j = 1? j < num_buckets? j++) {
          if (random.next() < 1.0 / (j + 1)) b = j?
          }
          return b?
          }

          這個(gè)復(fù)雜度比割環(huán)法還要高,如何優(yōu)化?容易想到,rand < 1 / (j + 1)的概率肯定是相對(duì)小的,也就是說(shuō)隨著j的增大,發(fā)生重分布的key的比例越來(lái)越小,j可以不必逐次自增,而是跳躍前進(jìn),這也就是算法名稱(chēng)中"jump"一詞的由來(lái)。

          觀察上面的代碼,b表示k最后一跳的目的哈希桶的編號(hào),即滿足條件:

          ch(k, b + 1) ≠ ch(k, b) && ch(k, b + 1) = b

          假設(shè)k連續(xù)不跳變,直到增加到j(luò) + 1個(gè)桶才發(fā)生跳變,可知此概率為:

          [(b + 1)/(b + 2)] * [(b + 2)/(b + 3)] * ... * [(j - 1)/j] = (b + 1) / j

          或者表示為:

          P[j ≥ i] = P[ch(k, i) = ch(k, b + 1)] = (b + 1) / i

          圖示如下。

          那么,j最多可以直接跳到哪里才不至于漏掉原有的循環(huán)過(guò)程呢?容易得知,要滿足rand < (b + 1) / j,需要j < (b + 1) / rand,將其向下取整即可。改進(jìn)后的ch()函數(shù)如下。

          int ch(int k, int n) {
          random.seed(k);
          int b = -1, j = 0;
          while (j < n) {
          b = j;
          r = random.next();
          j = floor((b+1) / r);
          }
          return b;
          }

          將random替換為具體的LCG,就是本節(jié)開(kāi)頭的算法了。

          分析時(shí)間復(fù)雜度:對(duì)于任意一個(gè)k,在哈希桶數(shù)從1增加到n的過(guò)程中,發(fā)生跳躍的期望次數(shù)是1 / 2 + ... + 1 / i + ... + 1 / n。根據(jù)歐拉常數(shù)的定義,調(diào)和級(jí)數(shù)與自然對(duì)數(shù)的差值的極限會(huì)收斂到一個(gè)小數(shù),因此跳躍一致性哈希算法的復(fù)雜度是O(ln n),比割環(huán)法更優(yōu)。

          根據(jù)論文給出的實(shí)驗(yàn)數(shù)據(jù),跳躍一致性哈希產(chǎn)生的分布的標(biāo)準(zhǔn)差遠(yuǎn)遠(yuǎn)比割環(huán)法小,也就是非常均勻。

          隨著桶數(shù)量的增加,跳躍一致性哈希算法的執(zhí)行時(shí)間增長(zhǎng)也不明顯。

          另外,它不需要額外的數(shù)據(jù)結(jié)構(gòu),內(nèi)存占用極小(即論文標(biāo)題中所說(shuō)的minimal memory)。

          但是,它相對(duì)于割環(huán)法而言有個(gè)非常大的缺點(diǎn),即只能在哈希桶序列的尾部添加和刪除桶,而不能在中間增刪。顯而易見(jiàn),如果在中間增刪桶,由于桶的標(biāo)號(hào)是按自然順序來(lái)的,因此會(huì)導(dǎo)致后方所有桶的標(biāo)號(hào)發(fā)生變化,不再滿足一致性哈希的基本性質(zhì)。

          仍然考慮節(jié)點(diǎn)擴(kuò)縮容以及節(jié)點(diǎn)宕機(jī)時(shí)如何保證系統(tǒng)仍然可用的問(wèn)題。

          • 中繼——如果在尾部的哈希桶j + 1中查不到所需的數(shù)據(jù),就把請(qǐng)求轉(zhuǎn)發(fā)給ch(k, j)桶,即它的上一跳節(jié)點(diǎn)。

          • 雙寫(xiě)——每次寫(xiě)入數(shù)據(jù)時(shí),如果寫(xiě)入的是尾部的哈希桶j + 1,就另外寫(xiě)一份到ch(k, j);如果寫(xiě)入的是非尾部的哈希桶i,就另外寫(xiě)一份到i + 1。這樣,不管是哪個(gè)節(jié)點(diǎn)失敗,數(shù)據(jù)都不會(huì)丟失。

          Greenplum中的應(yīng)用

          Greenplum提供了一個(gè)為集群擴(kuò)容的工具gpexpand。在GP v5中,執(zhí)行g(shù)pexpand時(shí)需要將所有哈希分布改為隨機(jī)分布,按照新的集群規(guī)模重新根據(jù)hash key計(jì)算哈希值,再將數(shù)據(jù)重新均衡到各個(gè)segment節(jié)點(diǎn)上,相當(dāng)于進(jìn)行了一次完全的shuffle,如下圖所示。

          這種方式的缺點(diǎn)顯而易見(jiàn):集群在擴(kuò)容期間處于不可用狀態(tài),數(shù)據(jù)交換量巨大。并且在數(shù)據(jù)由隨機(jī)分布轉(zhuǎn)為新的哈希分布之前,無(wú)法利用數(shù)據(jù)的本地性信息做查詢優(yōu)化,拖累性能。

          在GP v6中,通過(guò)將跳躍一致性哈希引入gpexpand,實(shí)現(xiàn)了完全在線、高性能的集群擴(kuò)容方式。如下圖所示,將集群由3節(jié)點(diǎn)擴(kuò)容到4節(jié)點(diǎn),只有1/4的數(shù)據(jù)需要重分布。

          GP v6的跳躍一致性哈希實(shí)現(xiàn)與Google原版完全相同。

          另外,如何保證那些沒(méi)有重分布完畢的表被正確地查詢呢?GP v6在catalog表gp_distribution_policy里加入了一個(gè)新的字段numsegments,表示一張表的數(shù)據(jù)分布在前numsegments個(gè)節(jié)點(diǎn)上。因此,就算擴(kuò)容的過(guò)程中有事務(wù)正在運(yùn)行,只要numsegments沒(méi)有改變,就仍然只在原有節(jié)點(diǎn)上執(zhí)行查詢。

          最后,可以通過(guò)全局配置gp_use_legacy_hashops設(shè)定是否改回舊版的取模哈希方式,默認(rèn)當(dāng)然為false。


          一萬(wàn)五千字詳解HTTP協(xié)議

          Spark如何協(xié)調(diào)來(lái)完成整個(gè)Job的運(yùn)行詳解

          最新Hive/Hadoop高頻面試點(diǎn)小集合

          歡迎點(diǎn)贊+收藏+轉(zhuǎn)發(fā)朋友圈素質(zhì)三連

          文章不錯(cuò)?點(diǎn)個(gè)【在看】吧! 
          瀏覽 28
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色视频免费收看黄色视频免费收看 | 日本A在线看 | 91人妻成人精品一区二区 | 色老扳AV | 99精品人妻无码一区二区三区 |