[白話解析] 深入淺出一致性Hash原理

0x00 摘要

一致性雜湊演算法是分散式系統中常用的演算法。但相信很多朋友都是知其然而不知其所以然。本文將盡量使用易懂的方式介紹一致性雜湊原理,並且通過具體應用場景來幫助大家深入這個概念。

0x01. 概念&原理

Hash,一般翻譯做雜湊、雜湊,或音譯為雜湊,是把任意長度的輸入(又叫做預對映pre-image)通過雜湊演算法變換成固定長度的輸出,該輸出就是雜湊值。

一致性雜湊演算法在1997年由麻省理工學院的Karger等人在解決分散式Cache中提出的,主要是為了解決因特網中的熱點(Hot spot)問題。目前這一思想已經擴充套件到其它的領域,並且在實踐中得到了很大的發展。

1. 與經典雜湊方法的對比

  • 經典雜湊方法:總是假設記憶體位置的數量是已知且固定不變的。因為hash對映依賴節點/記憶體位置,所以如果需要變化叢集,需要重新計算每一個key的雜湊值。雜湊表(伺服器數量)大小的變更實際上干擾了 所有對映。

  • 一致性雜湊:某種虛擬環結構。位置數量不再固定,環有無限數量的點,伺服器節點可以放置在環上的隨機位置。雜湊表(伺服器數量)大小改變會導致 只有 一部分請求(相對於環分配因子)會受到特定的環變更的影響

2. 通俗理解一致性雜湊的關鍵點:

從拗口的技術術語來解釋,一致性雜湊的技術關鍵點是:按照常用的hash演算法來將對應的key雜湊到一個具有2^32次方個桶的空間中,即0 ~(2^32)-1的數字空間。我們可以將這些數字頭尾相連,想象成一個閉合的環形。

用通俗白話來理解,這個關鍵點就是:在部署伺服器的時候,伺服器的序號空間已經配置成了一個固定的非常大的數字 1~2^32。伺服器可以分配為 1~2^32 中任一序號。這樣伺服器叢集可以固定大多數演算法規則 (因為序號空間是演算法的重要引數),這樣面對擴容等變化只是對部分演算法規則做調整。具體會參見後面例項詳細說明。

3. 一致性雜湊如何處理請求

如何決定哪個請求將由哪個伺服器節點來處理?

從理論上來說,每個伺服器節點“擁有”雜湊環的一個區間,進入該區間的任何請求將由同一伺服器節點來處理。

我們假設環是有序的,以便環的順時針遍歷與位置地址的遞增順序對應,那麼每個請求可以由最先出現在該順時針遍歷中的那個伺服器節點來處理。也就是說,地址高於請求地址的第一個伺服器節點負責處理該請求。如果請求地址高於最高定址節點,它由最小地址的伺服器節點來處理,因為環遍歷以圓形方式進行。

4. 異常處理/變化應對

如果其中一個伺服器節點出現故障,下一個伺服器節點的區間就變寬,進入該區間的任何請求都將進入到新的伺服器節點。這時候應該如何處理這些異常的請求?

一致性Hash的優勢就在這裡體現:需要重新分配的是僅僅這一個區間(與出現故障的伺服器節點對應),雜湊環的其餘部分和請求/節點分配仍然不受影響。

0x02. 具體應用場景(通過名著水滸傳為例來闡釋)

大家都知道,梁山泊山下有四個酒店。分別是: 東山酒店 / 西山酒店 / 南山酒店 / 北山酒店

那麼這四個酒店如何分配客人入住呢? 這裡就能用到Hash演算法,也能看到一致性雜湊的好處。

1. 經典演算法:

梁山4個酒店,按照順序其序號是1,2,3,4。

雜湊函式:客人姓名筆畫 / 4得到一個餘數,客人按照餘數分配到這4個酒店中

如果減少一個酒店,雜湊函式就變成: 客人按照姓名筆畫 / 3,然後客人按照這個新餘數來分配酒店。所有客人都得重新分配酒店

如果增加一個酒店,雜湊函式就變成: 客人按照姓名筆畫 / 5,然後客人按照這個新餘數來分配酒店。所有客人都得重新分配酒店

可以看到,如果有容量變化,則雜湊函式和分配規則都要改變,這樣就對整體機制造成了傷害。

2. 一致性演算法:

預先就把伺服器的序號空間(現在~未來)想好了,定為100個桶。就是在未來可見的年份內,100個肯定夠了(l梁山無論怎麼擴大生產規模,哪怕擴招了10000個頭領,山下也沒有開設100個酒店的可能)。

雜湊函式(這個固定不變):

客人姓名筆畫/100. 這個是固定不變的! 因為100這個序號空間固定了,所以雜湊函式和分配規則都基本固定了。

酒店/客人分配規則如下(這個會根據容量變化做相應微調):

  • 酒店1負責 hash(x)--> 1~20,即客人姓名筆畫/100位於1~20之間。

  • 酒店2負責 hash(x)--> 21~40,即客人姓名筆畫/100位於21~40之間。

  • 酒店3負責 hash(x)--> 41~60,即客人姓名筆畫/100位於41~60之間。

  • 酒店4負責 hash(x)--> 61~100,即客人姓名筆畫/100位於61~100之間。

客人住店規則如下(這個固定不變):

  • 客人來了,姓氏筆畫/100,得到餘數。去餘數對應的酒店住。比如餘數3住到酒店1,餘數22則住到酒店2......

  • 如果該酒店出問題關門了,就去比所有 "比餘數大的酒店" 中最小那個住。以此類推。比如酒店1掛了,就去酒店2,酒店2掛了去3。

  • 如果最大酒店也出問題關門了,就轉圈回到最小酒店住。即如果酒店4掛了去酒店1.

異常處理(擴容或者宕機):

  • 減少酒店。如果酒店3掛了,則原來去酒店3的客人去酒店4,原來去酒店4的客人還是酒店4. 這樣只有酒店4受到影響,1,2號酒店客人不用搬家。

  • 增加酒店。如果增加了一個酒店5.則需要對 酒店/客人分配規則 做改變。讓4號酒店負責61~80,5號酒店負責81~100。這樣4號點原有部分客人要遷移到5號。

關鍵點:

可以看出來,關鍵在於伺服器的序號空間早就確定了是一個以後也不會修改的大數字100。當然這是梁山。對於其他真實案例可能是2^32。這樣hash函式 (因為序號空間是演算法一個重要引數) 可以保持不變,只有"分配規則" 需要根據實際系統容量做相應微調。從而對整體系統影響較小。

當然具體分配酒店的規則演算法,是可以融入到hash中。即酒店號碼可能就是21,41,61....

0x03. 參考

https://blog.csdn.net/gerryke/article/details/53939212

https://blog.csdn.net/cb_lcl/article/details/81448570

https://www.iteblog.com/archives/2499.html

http://www.zsythink.net/archives/1182/

https://www.sohu.com/a/239283928_46