https://blog.phpha.com/backup/archives/1303.html
當Memcached服務器數量固定時,普通Hash分佈可以很好的運作。但是當服務器數量發生改變時,問題就出來了。因爲同一個KEY經Hash算法處理後,與服務器數量取模,會導致結果與服務器數量未變化時不同,這就導致之前保存的數據丟失。採取一致性Hash分佈可以有效的解決這個問題,把丟失的數據減到最小(注意這裏並沒有說完全不丟失)。
一致性Hash分佈算法分4個步驟:
步驟1:將一個32位整數[0 ~ (2^32-1)]想象成一個環,0 作爲開頭,(2^32-1) 作爲結尾,當然這只是想象。
步驟2:通過Hash函數把KEY處理成整數。這樣就可以在環上找到一個位置與之對應。
步驟3:把Memcached服務器羣映射到環上,使用Hash函數處理服務器對應的IP地址即可。
步驟4:把數據映射到Memcached服務器上。查找一個KEY對應的Memcached服務器位置的方法如下:從當前KEY的位置,沿着圓環順時針方向出發,查找位置離得最近的一臺Memcached服務器,並將KEY對應的數據保存在此服務器上。
說明:這樣一來,當添加或移除某一臺服務器時,受影響的數據範圍變的更小了。具體可以畫個圖更便於理解,這裏我就不畫了。
[3]一致性Hash分佈算法實例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
<?php
/**
* 一致性Hash分佈
* 天涯PHP博客
* http://blog.phpha.com
*/
class
FlexiHash{
//服務器列表
private
$serverList =
array();
//記錄是否已經排序
private
$isSorted =
FALSE;
//添加一臺服務器
public
function addServer($server){
$hash
= $this->mHash($server);
if(!isset($this->serverList[$hash])){
$this->serverList[$hash]
= $server;
}
//需要重新排序
$this->isSorted
= FALSE;
return
TRUE;
}
//移除一臺服務器
public
function removeServer($server){
$hash
= $this->mHash($server);
if(isset($this->serverList[$hash])){
unset($this->serverList[$hash]);
}
//需要重新排序
$this->isSorted
= FALSE;
return
TRUE;
}
//在當前服務器列表查找合適的服務器
public
function lookup($key){
$hash
= $this->mHash($key);
//先進行倒序排序操作
if(!$this->isSorted){
krsort($this->serverList,
SORT_NUMERIC);
$this->isSorted
= TRUE;
}
//圓環上順時針方向查找當前KEY緊鄰的一臺服務器
foreach($this->serverList
as $pos
=>
$server){
if($hash
>= $pos)
return $server;
}
//沒有找到則返回順時針方向最後一臺服務器
return
$this->serverList[count($this->serverList)
- 1];
}
//Hash函數
private
function mHash($key){
$md5
= substr(md5($key),
0,
8);
$seed
= 31;
$hash
= 0;
for($i
= 0;
$i <
8;
$i++){
$hash
= $hash
* $seed
+ ord($md5{$i});
$i++;
}
return
$hash &
0x7FFFFFFF;
}
}
?>
|
說明:其整體查找思路,已經在前面的一致性Hash分佈部分進行了介紹,需要補充的是每次添加或移除服務器後需要對服務器列表這個序列就行一次排序。
下面是對上面的一致性Hash分佈實例的相關測試代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
<?php
/**
* 一致性Hash分佈測試代碼
* 天涯PHP博客
* http://blog.phpha.com
*/
$hserver
= new
FlexiHash();
//初始5臺服務器
$hserver->addServer("192.168.1.1");
$hserver->addServer("192.168.1.2");
$hserver->addServer("192.168.1.3");
$hserver->addServer("192.168.1.4");
$hserver->addServer("192.168.1.5");
echo
"save key1 in server: ",
$hserver->lookup('key1'),
"<br/>";
echo
"save key2 in server: ",
$hserver->lookup('key2'),
"<br/>";
echo
'===============================================<br/>';
//移除1臺服務器
$hserver->removeServer("192.168.1.4");
echo
"save key1 in server: ",
$hserver->lookup('key1'),
"<br/>";
echo
"save key2 in server: ",
$hserver->lookup('key2'),
"<br/>";
echo
'===============================================<br/>';
//添加1臺服務器
$hserver->addServer('192.168.1.6');
echo
"save key1 in server: ",
$hserver->lookup('key1'),
"<br/>";
echo
"save key2 in server: ",
$hserver->lookup('key2');
?>
//測試結果如下:
save
key1 in
server:
192.168.1.4
save
key2 in
server:
192.168.1.2
==================================
save
key1 in
server:
192.168.1.3
save
key2 in
server:
192.168.1.2
==================================
save
key1 in
server:
192.168.1.3
save
key2 in
server:
192.168.1.2
|