參考文章php
http://blog.chinaunix.net/uid-20498361-id-4303232.htmlhtml
http://blog.csdn.net/kongqz/article/details/6695417算法
https://my.oschina.net/goal/blog/203593?p=1服務器
一直認爲一致性hash是個很神奇的東東,技術大牛的想法果真與衆不同。優化
下列代碼並無做優化處理,僅供參考理解ketama算法,固然也有助於本身理解記憶。若是有錯誤的地方歡迎指出ui
1 function addServer($hostName = array()){ 2 $serverList = array(); 3 foreach($hostName as $val) { 4 for($i = 0;$i<40;$i++) { 5 $digest = md5($val.'_'.$i, true); 6 for ($h = 0; $h < 4; $h++) { 7 $hash = (ord($digest[3 + $h * 4]) << 24) | (ord($digest[2 + $h * 4]) << 16) 8 | (ord($digest[1 + $h * 4]) << 8) | ord($digest[$h * 4]); 9 $serverList[$val][$i*4+$h] = $hash; 10 } 11 } 12 } 13 return $serverList; 14 } 15 //md5的緣由是怕用戶輸入前綴一致 hash同樣 分佈不均勻?? 16 function getKeyHash($str){ 17 $digest = md5($str,1); 18 return (ord($digest[3])<<24) | (ord($digest[2])<<16) | (ord($digest[3])<<8) | (ord($digest[0])); 19 } 20 21 22 function findKey($str){ 23 $hostName = array( 24 '10.100.20.1', 25 '10.100.20.2', 26 '10.100.20.3', 27 '10.100.20.4', 28 '10.100.20.5', 29 '10.100.20.6', 30 ); 31 $serverList = addServer($hostName); 32 $serverHash = array(); 33 foreach($serverList as $server){ 34 $serverHash = array_merge($serverHash,$server); 35 } 36 sort($serverHash); 37 $i = 0; 38 $len = count($serverHash); 39 $keyHash = getKeyHash($str); 40 if($keyHash>$serverHash[$len-1]){ 41 foreach($serverList as $key=>$val){ 42 if(in_array($serverHash[$len-1],$val)){ 43 return $key; 44 } 45 } 46 return 0; 47 } 48 while($i<$len){ 49 $curr = $serverHash[$i]; 50 $next = $serverHash[++$i]; 51 if($next>=$keyHash && $keyHash>=$curr){ 52 foreach($serverList as $key=>$val){ 53 if(in_array($serverHash[$i],$val)){ 54 return $key; 55 } 56 } 57 } 58 } 59 return false; 60 } 61 $result = array(); 62 for($i=1;$i<1000;$i++){ 63 $tmp = findKey('key'.$i); 64 if(isset($result[$tmp])){ 65 $result[$tmp]++; 66 }else{ 67 $result[$tmp] = 0; 68 } 69 } 70 print_r($result);
上面輸出結果以下,因此說該算法分佈仍是至關均勻的。spa
[root@silence suanfa]# php ketama.php
Array
(
[10.100.20.6] => 149
[10.100.20.5] => 162
[10.100.20.4] => 171
[10.100.20.2] => 146
[10.100.20.3] => 196
[10.100.20.1] => 169
).net
查找鍵值所在服務器能夠採用下列折半的方式unix
1 $rangeArr = 2 array ( 3 0 => 19, 4 1 => 99, 5 2 => 47, 6 3 => 49, 7 4 => 74, 8 5 => 77, 9 6 => 45, 10 7 => 95, 11 8 => 87, 12 9 => 15, 13 10 => 14, 14 11 => 29, 15 12 => 57, 16 13 => 3, 17 14 => 8, 18 15 => 38, 19 16 => 28, 20 17 => 67, 21 18 => 55, 22 19 => 43, 23 20 => 5, 24 21 => 25, 25 22 => 48, 26 23 => 32, 27 24 => 58, 28 25 => 90, 29 26 => 27, 30 27 => 50, 31 28 => 6, 32 29 => 63, 33 30 => 65, 34 31 => 81, 35 32 => 51, 36 33 => 52, 37 34 => 66, 38 35 => 30, 39 36 => 56, 40 37 => 54, 41 38 => 86, 42 39 => 84, 43 40 => 83, 44 41 => 64, 45 42 => 42, 46 43 => 26, 47 44 => 46, 48 45 => 79, 49 46 => 69, 50 47 => 98, 51 48 => 24, 52 49 => 37, 53 ); 54 sort($rangeArr); 55 function findPoint($num,$rangeArr){ 56 $len = count($rangeArr); 57 if($num> $rangeArr[$len-1] || $num<$rangeArr[0]){ 58 return $num.'in'.$rangeArr[0]; 59 } 60 if($len == 2){ 61 return $num.'in'.$rangeArr[1]; 62 } 63 if($len == 1){ 64 return $num.'in'.$rangeArr[0]; 65 } 66 $half =(int)floor($len/2); 67 if($num<$rangeArr[$half]){ 68 $tmp = array_slice($rangeArr,0,$half+1); 69 return findPoint($num,$tmp); 70 }else{ 71 $tmp = array_slice($rangeArr,$half,$len-$half); 72 return findPoint($num,$tmp); 73 } 74 } 75 echo findPoint(4,$rangeArr);exit;