(跳躍)一致性哈希及其在Greenplum中的應用

前言

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

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

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

顧名思義串纺,割環(huán)法將整個哈侠雎茫空間組織成一個首尾相接的圓環(huán),一般設為[0, 232 - 1]造垛。以分布式K-V存儲為例魔招,哈希桶即為存儲節(jié)點。將節(jié)點N的編號或IP等按哈希函數hash(N)映射在環(huán)上五辽,再將數據的key按同樣的哈希函數hash(k)映射在環(huán)上办斑,數據就會存儲在環(huán)上以順時針方向遍歷找到的第一個節(jié)點。當節(jié)點擴容或縮容時杆逗,仍然按照順時針原則乡翅,將受到影響的區(qū)間內的數據重新分布到相鄰的節(jié)點上去,達到增量更新的目的罪郊,即滿足單調性蠕蚜。以下3張圖能夠簡單地說明。

雖然哈希函數的結果是均勻的悔橄,但節(jié)點映射在環(huán)上可能不均勻靶累,節(jié)點數越少,數據傾斜的可能性就越大癣疟。解決此問題的方法是將物理節(jié)點虛擬成多個影子節(jié)點挣柬,數據經過哈希后按順時針原則落到影子節(jié)點指向的物理節(jié)點上。如果我們想要人為干預各節(jié)點上數據量的權重睛挚,還可以指定不同的影子節(jié)點數量邪蛔。如下圖所示,影子節(jié)點數量為3:2:2:1扎狱。

虛擬節(jié)點擴縮容時的數據遷移方法與僅采用物理節(jié)點相同侧到,因此調整權重值也會觸發(fā)數據遷移。

對于有N個桶和K個鍵的一致性哈希方案淤击,其時間復雜度是:

  • 添加匠抗、刪除節(jié)點——O(K / N + logN);
  • 添加污抬、刪除key——O(logN)戈咳。

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

最后有一個小問題:節(jié)點擴縮容以及節(jié)點宕機時如何保證系統(tǒng)仍然可用呢删铃?有兩種直接的思路:

  • 中繼——如果在某個節(jié)點上查不到所需的數據耳贬,就把請求轉發(fā)給該節(jié)點的順時針方向下一個節(jié)點進行處理。
  • 雙寫——每次寫入數據時猎唁,都另外寫一份到目標節(jié)點的順時針方向下一個節(jié)點咒劲。

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

跳躍一致性哈希

這個算法比較年輕蛔屹,在2014年由Google的大佬John Lamping和Eric Veach提出削樊,原始論文見<<A Fast, Minimal Memory, Consistent Hash Algorithm>>。它的實現非常簡潔兔毒,僅有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?
}

看官可能還無法理解為什么能這樣實現育叁,接下來重走一遍論文的推導思路迅脐。

假設最終要求的滿足平衡性和單調性的哈希函數是ch(k, n)(k為數據的鍵,n為哈希桶的數量)豪嗽,有如下簡單的遞推關系:

  • 當n = 1時谴蔑,所有key都要映射到同一個桶中,即ch(k, 1) = 0龟梦;
  • 當n = 2時隐锭,為保證均勻性,需有K / 2個key分別映射到兩個桶中(K是key的總數量)变秦,故K / 2個key需要重新映射成榜;
  • ......
  • 當桶數量由n變?yōu)閚 + 1時,有K / (n + 1)個key需要重新映射蹦玫。

那么該如何決定哪些key被重新映射到新的桶中呢赎婚?答案是采用線性同余法(LCG)生成的偽隨機數決定。關于線性同余法樱溉,可參見筆者之前寫過的這篇文章挣输,上文中的magic number 2862933555777941757就是線性同余法的乘數a。

以k作為種子生成一個偽隨機數序列福贞,可以保證對于確定的k撩嚼,ch(k, n)的結果也是確定的,進而使用條件rand < 1 / (j + 1)即可保證哈希桶由j個變?yōu)閖 + 1個時挖帘,有1 / (j + 1)比例的數據會重新映射完丽。

此時ch()函數的邏輯如下,時間復雜度顯然為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?
}

這個復雜度比割環(huán)法還要高逻族,如何優(yōu)化?容易想到骄崩,rand < 1 / (j + 1)的概率肯定是相對小的聘鳞,也就是說隨著j的增大薄辅,發(fā)生重分布的key的比例越來越小,j可以不必逐次自增抠璃,而是跳躍前進站楚,這也就是算法名稱中"jump"一詞的由來。

觀察上面的代碼搏嗡,b表示k最后一跳的目的哈希桶的編號窿春,即滿足條件:

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

假設k連續(xù)不跳變,直到增加到j + 1個桶才發(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)過程呢纽甘?容易得知良蛮,要滿足rand < (b + 1) / j,需要j < (b + 1) / rand悍赢,將其向下取整即可决瞳。改進后的ch()函數如下。

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,在哈希桶數從1增加到n的過程中赏迟,發(fā)生跳躍的期望次數是1 / 2 + ... + 1 / i + ... + 1 / n屡贺。根據歐拉常數的定義,調和級數與自然對數的差值的極限會收斂到一個小數锌杀,因此跳躍一致性哈希算法的復雜度是O(ln n)甩栈,比割環(huán)法更優(yōu)。

根據論文給出的實驗數據糕再,跳躍一致性哈希產生的分布的標準差遠遠比割環(huán)法小量没,也就是非常均勻。

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

另外,它不需要額外的數據結構猾担,內存占用極邢啤(即論文標題中所說的minimal memory)。

但是绑嘹,它相對于割環(huán)法而言有個非常大的缺點妓蛮,即只能在哈希桶序列的尾部添加和刪除桶,而不能在中間增刪圾叼。顯而易見蛤克,如果在中間增刪桶,由于桶的標號是按自然順序來的夷蚊,因此會導致后方所有桶的標號發(fā)生變化构挤,不再滿足一致性哈希的基本性質。

仍然考慮節(jié)點擴縮容以及節(jié)點宕機時如何保證系統(tǒng)仍然可用的問題惕鼓。

  • 中繼——如果在尾部的哈希桶j + 1中查不到所需的數據筋现,就把請求轉發(fā)給ch(k, j)桶,即它的上一跳節(jié)點箱歧。
  • 雙寫——每次寫入數據時矾飞,如果寫入的是尾部的哈希桶j + 1,就另外寫一份到ch(k, j)呀邢;如果寫入的是非尾部的哈希桶i洒沦,就另外寫一份到i + 1。這樣价淌,不管是哪個節(jié)點失敗申眼,數據都不會丟失。

Greenplum中的應用

Greenplum提供了一個為集群擴容的工具gpexpand蝉衣。在GP v5中括尸,執(zhí)行gpexpand時需要將所有哈希分布改為隨機分布,按照新的集群規(guī)模重新根據hash key計算哈希值病毡,再將數據重新均衡到各個segment節(jié)點上濒翻,相當于進行了一次完全的shuffle,如下圖所示啦膜。

這種方式的缺點顯而易見:集群在擴容期間處于不可用狀態(tài)有送,數據交換量巨大。并且在數據由隨機分布轉為新的哈希分布之前功戚,無法利用數據的本地性信息做查詢優(yōu)化娶眷,拖累性能。

在GP v6中啸臀,通過將跳躍一致性哈希引入gpexpand届宠,實現了完全在線、高性能的集群擴容方式乘粒。如下圖所示豌注,將集群由3節(jié)點擴容到4節(jié)點,只有1/4的數據需要重分布灯萍。

GP v6的跳躍一致性哈希實現與Google原版完全相同秽誊,可參見這里

另外努酸,如何保證那些沒有重分布完畢的表被正確地查詢呢?GP v6在catalog表gp_distribution_policy里加入了一個新的字段numsegments药薯,表示一張表的數據分布在前numsegments個節(jié)點上。因此救斑,就算擴容的過程中有事務正在運行童本,只要numsegments沒有改變,就仍然只在原有節(jié)點上執(zhí)行查詢脸候。

最后穷娱,可以通過全局配置gp_use_legacy_hashops設定是否改回舊版的取模哈希方式,默認當然為false运沦。

The End

最近天氣有點冷泵额,民那注意保暖。

晚安晚安携添。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末嫁盲,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子薪寓,更是在濱河造成了極大的恐慌亡资,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件向叉,死亡現場離奇詭異锥腻,居然都是意外死亡,警方通過查閱死者的電腦和手機母谎,發(fā)現死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門瘦黑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奇唤,你說我怎么就攤上這事幸斥。” “怎么了咬扇?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵甲葬,是天一觀的道長。 經常有香客問我懈贺,道長经窖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任梭灿,我火速辦了婚禮画侣,結果婚禮上,老公的妹妹穿的比我還像新娘堡妒。我一直安慰自己配乱,他們只是感情好,可當我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著搬泥,像睡著了一般桑寨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上佑钾,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天西疤,我揣著相機與錄音,去河邊找鬼休溶。 笑死,一個胖子當著我的面吹牛扰她,可吹牛的內容都是我干的兽掰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼徒役,長吁一口氣:“原來是場噩夢啊……” “哼孽尽!你這毒婦竟也來了?” 一聲冷哼從身側響起忧勿,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤杉女,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鸳吸,有當地人在樹林里發(fā)現了一具尸體熏挎,經...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年晌砾,在試婚紗的時候發(fā)現自己被綠了坎拐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡养匈,死狀恐怖哼勇,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情呕乎,我是刑警寧澤积担,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站猬仁,受9級特大地震影響帝璧,放射性物質發(fā)生泄漏。R本人自食惡果不足惜逐虚,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一聋溜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叭爱,春花似錦撮躁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杨帽。三九已至,卻和暖如春嗤军,著一層夾襖步出監(jiān)牢的瞬間注盈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工叙赚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留老客,地道東北人。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓震叮,卻偏偏與公主長得像胧砰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子苇瓣,可洞房花燭夜當晚...
    茶點故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內容