最近有小夥伴跑過來問什麼是Hash一致性演算法,說面試的時候被問到了,因為不瞭解,所以就沒有回答上,問我有沒有相應的學習資料推薦,當時上班,沒時間回覆,晚上回去了就忘了這件事,今天突然看到這個,加班為大家整理一下什麼是Hash一致性演算法,希望對大家有幫助!
經常閱讀我文章的小夥伴應該都很熟悉我寫文章的套路,上來就是先要問一句為什麼?也就是為什麼要有Hash一致性演算法?就像以前介紹為什麼要有Spring一樣,首先會以歷史的角度或者專案發展的角度來分析,今天的分享還是一樣的套路,先從歷史的角度來一步步分析,探討一下到底什麼是Hash一致性演算法!
一、Redis叢集的使用
我們在使用Redis的時候,為了保證Redis的高可用,提高Redis的讀寫效能,最簡單的方式我們會做主從複製,組成Master-Master或者Master-Slave的形式,或者搭建Redis叢集,進行資料的讀寫分離,類似於資料庫的主從複製和讀寫分離。如下所示:

假設,我們有一個社交網站,需要使用Redis儲存圖片資源,儲存的格式為鍵值對,key值為圖片名稱,value為該圖片所在檔案伺服器的路徑,我們需要根據檔名查詢該檔案所在檔案伺服器上的路徑,資料量大概有2000W左右,按照我們約定的規則進行分庫,規則就是隨機分配,我們可以部署8臺快取伺服器,每臺伺服器大概含有500W條資料,並且進行主從複製,示意圖如下:

由於規則是隨機的,所有我們的一條資料都有可能儲存在任何一組Redis中,例如上圖我們使用者查詢一張名稱為"a.png"的圖片,由於規則是隨機的,我們不確定具體是在哪一個Redis伺服器上的,因此我們需要進行1、2、3、4,4次查詢才能夠查詢到(也就是遍歷了所有的Redis伺服器),這顯然不是我們想要的結果,有了解過的小夥伴可能會想到,隨機的規則不行,可以使用類似於資料庫中的分庫分表規則:按照Hash值、取模、按照類別、按照某一個欄位值等等常見的規則就可以出來了!好,按照我們的主題,我們就使用Hash的方式。
二、為Redis叢集使用Hash
可想而知,如果我們使用Hash的方式,每一張圖片在進行分庫的時候都可以定位到特定的伺服器,示意圖如下:

上圖中,假設我們查詢的是"a.png",由於有4臺伺服器(排除從庫),因此公式為hash(a.png) % 4 = 2
,可知定位到了第2號伺服器,這樣的話就不會遍歷所有的伺服器,大大提升了效能!
三、使用Hash的問題
上述的方式雖然提升了效能,我們不再需要對整個Redis伺服器進行遍歷!但是,使用上述Hash演算法進行快取時,會出現一些缺陷,主要體現在伺服器數量變動的時候,所有快取的位置都要發生改變!
試想一下,如果4臺快取伺服器已經不能滿足我們的快取需求,那麼我們應該怎麼做呢?很簡單,多增加幾臺快取伺服器不就行了!假設:我們增加了一臺快取伺服器,那麼快取伺服器的數量就由4臺變成了5臺。那麼原本hash(a.png) % 4 = 2
的公式就變成了hash(a.png) % 5 = ?
, 可想而知這個結果肯定不是2的,這種情況帶來的結果就是當伺服器數量變動時,所有快取的位置都要發生改變!換句話說,當伺服器數量發生改變時,所有快取在一定時間內是失效的,當應用無法從快取中獲取資料時,則會向後端資料庫請求資料(還記得上一篇的《快取雪崩》嗎?)!
同樣的,假設4臺快取中突然有一臺快取伺服器出現了故障,無法進行快取,那麼我們則需要將故障機器移除,但是如果移除了一臺快取伺服器,那麼快取伺服器數量從4臺變為3臺,也是會出現上述的問題!
所以,我們應該想辦法不讓這種情況發生,但是由於上述Hash演算法本身的緣故,使用取模法進行快取時,這種情況是無法避免的,為了解決這些問題,Hash一致性演算法(一致性Hash演算法)誕生了!
四、一致性Hash演算法的神祕面紗
一致性Hash演算法也是使用取模的方法,只是,剛才描述的取模法是對伺服器的數量進行取模,而一致性Hash演算法是對2^32取模,什麼意思呢?簡單來說,一致性Hash演算法將整個雜湊值空間組織成一個虛擬的圓環,如假設某雜湊函式H的值空間為0-2^32-1(即雜湊值是一個32位無符號整形),整個雜湊環如下:

下一步將各個伺服器使用Hash進行一個雜湊,具體可以選擇伺服器的IP或主機名作為關鍵字進行雜湊,這樣每臺機器就能確定其在雜湊環上的位置,這裡假設將上文中四臺伺服器使用IP地址雜湊後在環空間的位置如下:

例如我們有Object A、Object B、Object C、Object D四個資料物件,經過雜湊計算後,在環空間上的位置如下:

五、一致性Hash演算法的容錯性和可擴充套件性
現假設Node C不幸當機,可以看到此時物件A、B、D不會受到影響,只有C物件被重定位到Node D。一般的,在一致性Hash演算法中,如果一臺伺服器不可用,則受影響的資料僅僅是此伺服器到其環空間中前一臺伺服器(即沿著逆時針方向行走遇到的第一臺伺服器)之間資料,其它不會受到影響,如下所示:

下面考慮另外一種情況,如果在系統中增加一臺伺服器Node X,如下圖所示:

綜上所述,一致性Hash演算法對於節點的增減都只需重定位環空間中的一小部分資料,具有較好的容錯性和可擴充套件性。
六、Hash環的資料傾斜問題
一致性Hash演算法在服務節點太少時,容易因為節點分部不均勻而造成資料傾斜(被快取的物件大部分集中快取在某一臺伺服器上)問題,例如系統中只有兩臺伺服器,其環分佈如下:

例如上面的情況,可以為每臺伺服器計算三個虛擬節點,於是可以分別計算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的雜湊值,於是形成六個虛擬節點:

七、總結
上文中,我們一步步分析了什麼是一致性Hash演算法,主要是考慮到分散式系統每個節點都有可能失效,並且新的節點很可能動態的增加進來的情況,如何保證當系統的節點數目發生變化的時候,我們的系統仍然能夠對外提供良好的服務,這是值得考慮的!
參考文章:
1、www.cnblogs.com/lpfuture/p/… 2、www.zsythink.net/archives/11…
