<?php
#inspired by 大型分布式网站设计与实践,及 https://github.com/Yikun/hashes/blob/master/virtual_consist_hash.py
/* 一致性哈希
* 1,哈希值应该是无符号整形,其值范围为0~2^32-1。
* 2,哈希环节点顺时针分布。
**/
$items = 1000000; #元素
$nodes = 5; #真实节点
$vnodes = 100; #虚拟节点
$ring = []; #哈希环节点数组
$hash2node = []; #哈希值映射到哈希环的数组
$nodes_stat = []; #哈希环分布统计数据
function _hash($value) {
$k = md5(strval($value));
return unpack("N", $k)[1]; // bigendian unsigned long 32bit
}
/* 返回key在哈希环中插入后的位置 */
function bisect_left($sorted_array, $key) {
$left = 0;
$right = count($sorted_array) - 1;
if ($key < $sorted_array[$left] || $key >= $sorted_array[$right]) {
/* 环起点位置 */
return 0;
}
while($right - $left > 1) {
$middle = intval(($left + $right) / 2);
if ($key <= $sorted_array[$middle]) {
$right = $middle;
} else {
$left = $middle;
}
}
return $right;
}
/*test bisect_left*/
//echo bisect_left([1,3,7,9,12,15], 10) , PHP_EOL;
echo "初始化哈希环",PHP_EOL;
echo sprintf("节点数 %d, 虚拟节点数 %d, 总节点数 %d, 储存键数量 %d\n", $nodes, $vnodes, $nodes*$vnodes, $items);
echo "初始化哈希节点统计数据",PHP_EOL;
for ($i = 0; $i < $nodes; $i++)
$nodes_stat[$i] = 0;
for ($i = 0; $i < $nodes; $i++) {
for ($j = 0; $j < $vnodes; $j++) {
$h = _hash(strval($i) . strval($j));
array_push($ring, $h);
$hash2node[$h] = $i;
}
}
echo "排序哈希环",PHP_EOL;
sort($ring);
echo "模拟键值分布",PHP_EOL;
# 模拟分布key到哈希环中
for ($k = 0; $k < $items; $k++) {
$h = _hash($k);
/* 找到这个键值在哈希环的节点位置 */
$n = bisect_left($ring, $h) % ($nodes * $vnodes);
//echo sprintf("element %d distributed at node %d with hash %d\n", $k, $n, $h);
$nodes_stat[$hash2node[$ring[$n]]]++;
}
$avg = $items/$nodes;
$max = max($nodes_stat);
$min = min($nodes_stat);
echo "\n哈希节点统计数据\n===",PHP_EOL;
echo sprintf("avg: %d\n", $avg);
echo sprintf("max: %0.2f 最大值与平均值的差异(%0.2f%%)\n", $max, ($max-$avg)*100.0/$avg);
echo sprintf("min: %0.2f 最小值与平均值的差异(%0.2f%%)\n", $min, ($avg-$min)*100.0/$avg);
echo "节点哈希值分布情况\n===\n";
for ($i = 0; $i < $nodes; $i++)
echo sprintf("node %d, numbers: %d\n", $i, $nodes_stat[$i]);
测试结果
初始化哈希环
节点数 5, 虚拟节点数 100, 总节点数 500, 储存键数量 1000000
初始化哈希节点统计数据
排序哈希环
模拟键值分布
哈希节点统计数据
===
avg: 200000
max: 210085.00 最大值与平均值的差异(5.04%)
min: 185845.00 最小值与平均值的差异(7.08%)
节点哈希值分布情况
===
node 0, numbers: 191739
node 1, numbers: 185845
node 2, numbers: 209369
node 3, numbers: 202962
node 4, numbers: 210085
引用