memcached 一致性哈希算法

本文轉載自:http://blog.csdn.net/kongqz/article/details/6695417node

1、概述web

  一、咱們的memcache客戶端使用了一致性hash算法ketama進行數據存儲節點的選擇。與常規的hash算法思路不一樣,只是對咱們要存儲數據的key進行hash計算,分配到不一樣節點存儲。一致性hash算法是對咱們要存儲數據的服務器進行hash計算,進而確認每一個key的存儲位置。算法

 

 二、常規hash算法的應用以及其弊端數組

    最常規的方式莫過於hash取模的方式。好比集羣中可用機器適量爲N,那麼key值爲K的的數據請求很簡單的應該路由到hash(K) mod N對應的機器。的確,這種結構是簡單的,也是實用的。可是在一些高速發展的web系統中,這樣的解決方案仍有些缺陷。隨着系統訪問壓力的增加,緩存系統不得不經過增長機器節點的方式提升集羣的相應速度和數據承載量。增長機器意味着按照hash取模的方式,在增長機器節點的這一時刻,大量的緩存命不中,緩存數據須要從新創建,甚至是進行總體的緩存數據遷移,瞬間會給DB帶來極高的系統負載,設置致使DB服務器宕機。緩存

  三、設計分佈式cache系統時,一致性hash算法能夠幫咱們解決哪些問題?服務器

   分佈式緩存設計核心點:在設計分佈式cache系統的時候,咱們須要讓key的分佈均衡,而且在增長cache server後,cache的遷移作到最少。負載均衡

   這裏提到的一致性hash算法ketama的作法是:選擇具體的機器節點不在只依賴須要緩存數據的key的hash自己了,而是機器節點自己也進行了hash運算分佈式

 

2、一致性哈希算法情景描述(轉載)編碼

 

一、 hash機器節點spa

 

首先求出機器節點的hash值(怎麼算機器節點的hash?ip能夠做爲hash的參數吧。。固然還有其餘的方法了),而後將其分佈到0~2^32的一個圓環上(順時針分佈)。以下圖所示:

 

集羣中有機器:A , B, C, D, E五臺機器,經過必定的hash算法咱們將其分佈到如上圖所示的環上。

 

二、訪問方式

若是有一個寫入緩存的請求,其中Key值爲K,計算器hash值Hash(K), Hash(K) 對應於圖 – 1環中的某一個點,若是該點對應沒有映射到具體的某一個機器節點,那麼順時針查找,直到第一次找到有映射機器的節點,該節點就是肯定的目標節點,若是超過了2^32仍然找不到節點,則命中第一個機器節點。好比 Hash(K) 的值介於A~B之間,那麼命中的機器節點應該是B節點(如上圖 )。

 

三、增長節點的處理

如上圖 – 1,在原有集羣的基礎上欲增長一臺機器F,增長過程以下:

計算機器節點的Hash值,將機器映射到環中的一個節點,以下圖:

 

 

增長機器節點F以後,訪問策略不改變,依然按照(2)中的方式訪問,此時緩存命不中的狀況依然不可避免,不能命中的數據是hash(K)在增長節點之前落在C~F之間的數據。儘管依然存在節點增長帶來的命中問題,可是比較傳統的 hash取模的方式,一致性hash已經將不命中的數據降到了最低。

 

Consistent Hashing最大限度地抑制了hash鍵的從新分布。另外要取得比較好的負載均衡的效果,每每在服務器數量比較少的時候須要增長虛擬節點來保證服務器能均勻的分佈在圓環上。由於使用通常的hash方法,服務器的映射地點的分佈很是不均勻。使用虛擬節點的思想,爲每一個物理節點(服務器)在圓上分配100~200個點。這樣就能抑制分佈不均勻,最大限度地減少服務器增減時的緩存從新分佈。用戶數據映射在虛擬節點上,就表示用戶數據真正存儲位置是在該虛擬節點表明的實際物理服務器上。
下面有一個圖描述了須要爲每臺物理服務器增長的虛擬節點。



3、以spymemcache源碼來演示虛擬節點應用

一、上邊描述的一致性Hash算法有個潛在的問題是:
     (1)、將節點hash後會不均勻地分佈在環上,這樣大量key在尋找節點時,會存在key命中各個節點的機率差異較大,沒法實現有效的負載均衡。
     (2)、若有三個節點Node1,Node2,Node3,分佈在環上時三個節點挨的很近,落在環上的key尋找節點時,大量key順時針老是分配給Node2,而其它兩個節點被找到的機率都會很小。

二、這種問題的解決方案能夠有:
     改善Hash算法,均勻分配各節點到環上;[引文]使用虛擬節點的思想,爲每一個物理節點(服務器)在圓上分配100~200個點。這樣就能抑制分佈不均勻,最大限度地減少服務器增減時的緩存從新分佈。用戶數據映射在虛擬節點上,就表示用戶數據真正存儲位置是在該虛擬節點表明的實際物理服務器上。

在查看Spy Memcached client時,發現它採用一種稱爲Ketama的Hash算法,以虛擬節點的思想,解決Memcached的分佈式問題。 

三、源碼說明

該client採用TreeMap存儲全部節點,模擬一個環形的邏輯關係。在這個環中,節點以前是存在順序關係的,因此TreeMap的key必須實現Comparator接口。
那節點是怎樣放入這個環中的呢?

 

 1     protected void setKetamaNodes(List<MemcachedNode> nodes) {
 2     TreeMap<Long, MemcachedNode> newNodeMap = new TreeMap<Long, MemcachedNode>();
 3     int numReps= config.getNodeRepetitions();
 4     for(MemcachedNode node : nodes) {
 5         // Ketama does some special work with md5 where it reuses chunks.
 6         if(hashAlg == HashAlgorithm.KETAMA_HASH) {
 7             for(int i=0; i<numReps / 4; i++) {
 8                 byte[] digest=HashAlgorithm.computeMd5(config.getKeyForNode(node, i));
 9                 for(int h=0;h<4;h++) {
10                     Long k = ((long)(digest[3+h*4]&0xFF) << 24)
11                         | ((long)(digest[2+h*4]&0xFF) << 16)
12                         | ((long)(digest[1+h*4]&0xFF) << 8)
13                         | (digest[h*4]&0xFF);
14                     newNodeMap.put(k, node);
15                     getLogger().debug("Adding node %s in position %d", node, k);
16                 }
17 
18             }
19         } else {
20             for(int i=0; i<numReps; i++) {
21                 newNodeMap.put(hashAlg.hash(config.getKeyForNode(node, i)), node);
22             }
23         }
24     }
25     assert newNodeMap.size() == numReps * nodes.size();
26     ketamaNodes = newNodeMap;


上面的流程大概能夠這樣概括:四個虛擬結點爲一組,以getKeyForNode方法獲得這組虛擬節點的name,Md5編碼後,每一個虛擬結點對應Md5碼16個字節中的4個,組成一個long型數值,作爲這個虛擬結點在環中的唯一key。第10行k爲何是Long型的呢?就是由於Long型實現了Comparator接口。

處理完正式結點在環上的分佈後,能夠開始key在環上尋找節點的遊戲了。
對於每一個key仍是得完成上面的步驟:計算出Md5,根據Md5的字節數組,經過Kemata Hash算法獲得key在這個環中的位置。

 

5、總結

一、一致性hash算法只是幫咱們減小cache集羣中的機器數量增減的時候,cache的數據能進行最少重建。只要cache集羣的server數量有變化,必然產生數據命中的問題

二、對於數據的分佈均衡問題,經過虛擬節點的思想來達到均衡分配。固然,咱們cache server節點越少就越須要虛擬節點這個方式來均衡負載。

三、咱們的cache客戶端根本不會維護一個map來記錄每一個key存儲在哪裏,都是經過key的hash和cacheserver(也許ip能夠做爲參數)的hash計算當前的key應該存儲在哪一個節點上。

相關文章
相關標籤/搜索