一致性雜湊演算法核心思想,就是通過構造乙個長度為2^32的整數環,這個環也被稱為一致性hash環(只是邏輯上的環),將快取伺服器的節點名稱的雜湊值均勻的分布在[0,2^32-1]的hash環上,然後根據需要快取的key值計算得到其hash值,然後在hash環上順時針查詢距離key的hash值最近的伺服器節點,完成key到伺服器的對映查詢;如下圖所示
hash演算法解決的是集群管理中請求訪問的路由的問題,一般是根據乙個請求的某個key值取餘,路由到相應的伺服器,比如,key的hashcode是52,伺服器的數目是3,取餘之後為1,則該key對應的節點是node1,由於hashcode隨機性比較強,所以使用餘數hash路由演算法就可以保證快取資料在整個快取伺服器集群中有比較均衡的分布,這就是餘數hash的演算法,但是這種演算法會有乙個問題就是集群擴容了或者伺服器下線了,那麼取餘的方式,原來key對映到節點可能不會是node1,可能會導致某個節點的請求壓力變大,一致性雜湊解決了普通餘數hash演算法伸縮性差的問題,可以保證在上線、下線伺服器的情況下盡量有多的請求命中原來路由到的伺服器。
一致性雜湊演算法的實現首先就是要考慮如何構造出乙個長度為2^32的整數環,採用什麼資料結構,才能使得執行的時間複雜度最低,常見的時間複雜度與時間效率的關係有如下的經驗規則:
o(1) < o(log2n) < o(n) < o(n * log2n) < o(n2) < o(n3) < 2n < 3n < n!
一般來說,前四個效率比較高,中間兩個差強人意,後三個比較差(只要n比較大,這個演算法就動不了了
treemap底層使用了紅黑樹,時間複雜度為o(logn),效率比較高,可以作為實現雜湊環的資料結構;hash演算法的選擇上,首先我們考慮簡單的string.hashcode()方法,這個演算法的缺點是,相似的字串如n1(10.0.0.0:91001),n2(10.0.0.0:91002),n3(10.0.0.0:91003),雜湊值也很相近,造成的結果是節點在hash環上分布很緊密,導致大部分key值落到了n0上,節點資源分布不均。一般我們採用fnv1_32_hash、ketama_hash等演算法,ketama_hash是memcache集群預設的實現方法,這些演算法效果要好得多,會使n0,n1,n2的hash值更均勻的分布在環上;
所以初步的一致性雜湊演算法的實現如下:
public class consistenthashloadbalancenovirtualnode
/*** 初始化雜湊環,迴圈計算每個node的雜湊值,放入treemap中
*/private void initalization()
}/**
* 根據資源key選擇返回相應的節點名稱
** @param key
* @return
*/public string selectnode(string key) else
} else
}/**
* 使用ketama_hash演算法重新計算雜湊值
** @param nodename
* @param number
* @return
*/private long calculatehash(string nodename, int number)
private byte md5(string nodename) catch (nosuchalgorithmexception e) catch (unsupportedencodingexception e)
return null;
}private void printtreenode() );
} else
}public static void main(string args) ;
consistenthashloadbalancenovirtualnode consistenthash = new consistenthashloadbalancenovirtualnode(nodes);
consistenthash.printtreenode();
}}執行結果:
192.168.2.3:8080==>1182102228
192.168.2.4:8080==>1563927337
192.168.2.1:8080==>2686712470
192.168.2.2:8080==>3540412423
ketama_hash解決了hash值分布不均的問題,但還存在乙個問題,如下圖,在沒有node3節點時,資源相對均勻的分布在上。增加了node3節點後,node1到node3節點中間的所有資源從node2遷移到了node3上。這樣,node0,node1儲存的資源多,node2,node3儲存的資源少,資源分布不均勻。
如何解決這個問題呢?可以引入虛擬節點概念,如將乙個真實節點node0對映成100個虛擬節點分布在hash環上,與這100個虛擬節點根據ketama_hash雜湊環匹配的資源都存到真實節點node0上。以相同的方式拆分虛擬節點對映到hash環上。當集群增加節點node3時,在hash環上增加node3拆分的100個虛擬節點,這新增的100個虛擬節點更均勻的分布在了雜湊環上,可能承擔了每個節點的部分資源,資源分布仍然保持均勻。
每個真實節點應該拆分成多少個虛擬節點?數量要合適才能保證負載分布的均勻,有乙個大致的規律,如下圖所示,y軸表示真實節點的數目,x軸表示需拆分的虛擬節點數目:
真實節點越少,所需闡發的虛擬節點越多,比如有10個真實節點,每個節點所需拆分的虛擬節點個數可能是100~200個,才能達到真正的負載均衡,帶有虛擬節點的實現如下:
public class consistenthashloadbalance
/*** 初始化雜湊環
*/private void initalization() }}
}private string selectnode(string key) else
} else
}private long gethash(string nodename, int number)
/*** 74 * md5加密
* 75 *
* 76 * @param str
* 77 * @return
* 78
*/private byte md5(string nodename) catch (nosuchalgorithmexception e) catch (unsupportedencodingexception e)
return null;
}/**
* 根據資源key選擇返回相應的節點名稱
** @param nodename
* @param index
* @return
*/private string getnodenamebyindex(string nodename, int index)
public void addnode(string node) }}
public void removenode(string node) }}
private void printtreenode() );
} else
}public static void main(string args)
}執行結果(部分):
192.168.2.4:8080==>18075595
192.168.2.1:8080==>18286704
192.168.2.1:8080==>35659769
192.168.2.2:8080==>43448858
192.168.2.1:8080==>44075453
192.168.2.3:8080==>47625378
192.168.2.4:8080==>52449361
一致性雜湊演算法原理分析及實現 一致性雜湊的理解
一致性雜湊 consistent hashing 首先,讓我們了解一下一致性雜湊解決了怎樣的實際問題,當我們有了n臺伺服器 cache 時,我們通常會採用如下的通用演算法 將物件 object 均勻對映分配到伺服器上,hash object n 試想一下,當其中的一台伺服器down機了,或是添置一台...
一致性雜湊的理解
參考 先說結論 一致性雜湊也是雜湊演算法的一種,只是為了解決普通雜湊因為節點的變動導致資料遷移過大的問題。其核心思想是將位址空間看做乙個環狀結構,將節點進行雜湊,使節點盡可能均勻的散落在環上。同時將資料進行雜湊,也將資料均勻的散落在環上,最後,將資料全都放置在離資料順時針方向 因為是混裝結構,順時針...
一致性雜湊演算法
好吧,我們決定打破這種基於資料項商業邏輯的劃分思維,來考慮一種基於 key 的劃分方式,這有些類似於後面介紹的資料庫水平分割槽 sharding 我們需要設計一種不依賴資料項內容的雜湊演算法,將所有資料項的 key 均衡分配在這三颱快取伺服器上。乙個簡單而有效的方法是 取餘 運算,這就像打撲克時的發...