一致性哈希算法及其在分佈式系統中的應用

摘要

本文將會從實際應用場景出發,介紹一致性哈希算法(Consistent Hashing)及其在分佈式系統中的應用。首先本文會描述一個在平常開發中常常會遇到的問題場景,藉此介紹一致性哈希算法以及這個算法如何解決此問題;接下來會對這個算法進行相對詳細的描述,並討論一些如虛擬節點等與此算法應用相關的話題。web

分佈式緩存問題

假設咱們有一個網站,最近發現隨着流量增長,服務器壓力愈來愈大,以前直接讀寫數據庫的方式不太給力了,因而咱們想引入Memcached做爲緩存機制。如今咱們一共有三臺機器能夠做爲Memcached服務器,以下圖所示。算法

很顯然,最簡單的策略是將每一次Memcached請求隨機發送到一臺Memcached服務器,可是這種策略可能會帶來兩個問題:一是同一份數據可能被存在不一樣的機器上而形成數據冗餘,二是有可能某數據已經被緩存可是訪問卻沒有命中,由於沒法保證對相同key的全部訪問都被髮送到相同的服務器。所以,隨機策略不管是時間效率仍是空間效率都很是很差。數據庫

要解決上述問題只需作到以下一點:保證對相同key的訪問會被髮送到相同的服務器。不少方法能夠實現這一點,最經常使用的方法是計算哈希。例如對於每次訪問,能夠按以下算法計算其哈希值:緩存

h = Hash(key) % 3服務器

其中Hash是一個從字符串到正整數的哈希映射函數。這樣,若是咱們將Memcached Server分別編號爲0、一、2,那麼就能夠根據上式和key計算出服務器編號h,而後去訪問。dom

這個方法雖然解決了上面提到的兩個問題,可是存在一些其它的問題。若是將上述方法抽象,能夠認爲經過:分佈式

h = Hash(key) % Nide

這個算式計算每一個key的請求應該被髮送到哪臺服務器,其中N爲服務器的臺數,而且服務器按照0 – (N-1)編號。函數

這個算法的問題在於容錯性和擴展性很差。所謂容錯性是指當系統中某一個或幾個服務器變得不可用時,整個系統是否能夠正確高效運行;而擴展性是指當加入新的服務器後,整個系統是否能夠正確高效運行。網站

現假設有一臺服務器宕機了,那麼爲了填補空缺,要將宕機的服務器從編號列表中移除,後面的服務器按順序前移一位並將其編號值減一,此時每一個key就要按h = Hash(key) % (N-1)從新計算;一樣,若是新增了一臺服務器,雖然原有服務器編號不用改變,可是要按h = Hash(key) % (N+1)從新計算哈希值。所以系統中一旦有服務器變動,大量的key會被重定位到不一樣的服務器從而形成大量的緩存不命中。而這種狀況在分佈式系統中是很是糟糕的。

一個設計良好的分佈式哈希方案應該具備良好的單調性,即服務節點的增減不會形成大量哈希重定位。一致性哈希算法就是這樣一種哈希方案。

一致性哈希算法

算法簡述

一致性哈希算法(Consistent Hashing)最先在論文《Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web》中被提出。簡單來講,一致性哈希將整個哈希值空間組織成一個虛擬的圓環,如假設某哈希函數H的值空間爲0 - 232-1(即哈希值是一個32位無符號整形),整個哈希空間環以下:

整個空間按順時針方向組織。0和232-1在零點中方向重合。

下一步將各個服務器使用H進行一個哈希,具體能夠選擇服務器的ip或主機名做爲關鍵字進行哈希,這樣每臺機器就能肯定其在哈希環上的位置,這裏假設將上文中三臺服務器使用ip地址哈希後在環空間的位置以下:

接下來使用以下算法定位數據訪問到相應服務器:將數據key使用相同的函數H計算出哈希值h,通根據h肯定此數據在環上的位置,今後位置沿環順時針「行走」,第一臺遇到的服務器就是其應該定位到的服務器。

例如咱們有A、B、C、D四個數據對象,通過哈希計算後,在環空間上的位置以下:

根據一致性哈希算法,數據A會被定爲到Server 1上,D被定爲到Server 3上,而B、C分別被定爲到Server 2上。

容錯性與可擴展性分析

下面分析一致性哈希算法的容錯性和可擴展性。現假設Server 3宕機了:

能夠看到此時A、C、B不會受到影響,只有D節點被重定位到Server 2。通常的,在一致性哈希算法中,若是一臺服務器不可用,則受影響的數據僅僅是此服務器到其環空間中前一臺服務器(即順着逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。

下面考慮另一種狀況,若是咱們在系統中增長一臺服務器Memcached Server 4:

此時A、D、C不受影響,只有B須要重定位到新的Server 4。通常的,在一致性哈希算法中,若是增長一臺服務器,則受影響的數據僅僅是新服務器到其環空間中前一臺服務器(即順着逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。

綜上所述,一致性哈希算法對於節點的增減都只需重定位環空間中的一小部分數據,具備較好的容錯性和可擴展性。

虛擬節點

一致性哈希算法在服務節點太少時,容易由於節點分部不均勻而形成數據傾斜問題。例如咱們的系統中有兩臺服務器,其環分佈以下:

此時必然形成大量數據集中到Server 1上,而只有極少許會定位到Server 2上。爲了解決這種數據傾斜問題,一致性哈希算法引入了虛擬節點機制,即對每個服務節點計算多個哈希,每一個計算結果位置都放置一個此服務節點,稱爲虛擬節點。具體作法能夠在服務器ip或主機名的後面增長編號來實現。例如上面的狀況,咱們決定爲每臺服務器計算三個虛擬節點,因而能夠分別計算「Memcached Server 1#1」、「Memcached Server 1#2」、「Memcached Server 1#3」、「Memcached Server 2#1」、「Memcached Server 2#2」、「Memcached Server 2#3」的哈希值,因而造成六個虛擬節點:

同時數據定位算法不變,只是多了一步虛擬節點到實際節點的映射,例如定位到「Memcached Server 1#1」、「Memcached Server 1#2」、「Memcached Server 1#3」三個虛擬節點的數據均定位到Server 1上。這樣就解決了服務節點少時數據傾斜的問題。在實際應用中,一般將虛擬節點數設置爲32甚至更大,所以即便不多的服務節點也能作到相對均勻的數據分佈。

總結

目前一致性哈希基本成爲了分佈式系統組件的標準配置,例如Memcached的各類客戶端都提供內置的一致性哈希支持。本文只是簡要介紹了這個算法,更深刻的內容能夠參看論文《Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web》,同時提供一個C語言版本的實現供參考。

相關文章
相關標籤/搜索