普通 Hash 分布算法的 PHP 实现
首先假设有 2 台服务器:127.0.0.1:11211 和 192.168.186.129:11211
当存储的 key 经过对 2 (2 台服务器)取模运算得出该 key 应该保存到的服务器:
<?php $server = array( array('host' => '127.0.0.1', 'port' => 11211), array('host' => '192.168.186.129', 'port' => 11211), ); $key = 'TheKey'; $value = 'TheValue'; //假设是两台服务器 $sc = $server[crc32($key) % 2]; var_dump($sc);//得出该 key 应保存在第一台服务器上 $mc = new Memcache(); $mc->connect($sc['host'], $sc['port']); $mc->set($key, $value);
var_dump($sc) 输出的结果是:
array 'host' => string '127.0.0.1' (length=9) 'port' => int 11211
此时使用 telnet 连接本机(127.0.0.1:11211)的 Memcached 服务器,get 该 key:
再当 key 应该存储到第二台服务器上时:
<?php $server = array( array('host' => '127.0.0.1', 'port' => 11211), array('host' => '192.168.186.129', 'port' => 11211), ); $key = 'TheKey%'; $value = 'TheValueSecond'; //假设是两台服务器 $sc = $server[crc32($key) % 2]; var_dump($sc);//得出该 key 应保存在第二台服务器上 $mc = new Memcache(); $mc->connect($sc['host'], $sc['port']); $mc->set($key, $value);
var_dump($sc) 输出的结果是:
array 'host' => string '192.168.186.129' (length=15) 'port' => int 11211
此时使用 telnet 连接虚拟机(192.168.186.129:11211)的 Memcached 服务器,get 该 key:
普通 Hash 分布的缺点是:当服务器数量发生变化时,同一个 key 经过 Hash 之后,与服务器取模的结果跟没有增加或减少服务器之前的结果可能会不一样。例如:原来有 8 台服务器,宕掉 1 台之后,还剩 7 台,则 8 台服务器 $key % 8 = 0,$key % 7 = 0,此时为命中(hits);如果 $key % 8 = 0,%key % 7 = 1,则此时 miss。
为了把丢失的数据减小到最少,可以使用 一致性哈希算法(Consistent Hashing)。
一致性哈希算法
step 1. 将一个 32 位的整数(0~2^32 - 1)想象成一个环,0 作为圆环的头,2^32 - 1 作为圆环的尾。
step 2. 通过 Hash 函数把 key 处理成整数:
$key1 = crc32($key1); $key2 = crc32($key2); $key3 = crc32($key3); $key4 = crc32($key4);
step 3. 把 Memcached 群映射到环上。使用 Hash 函数把服务器的 IP 地址处理成整数:
$server1 = crc32('127.0.0.1'); $server2 = crc32('192.168.186.129'); $server3 = crc32('192.168.186.130');
通过以上步骤,把 key 和 服务器都映射到环上。
step 4. 把数据映射到服务器上。
如图,key1 落在了 server 3 上,key 4 和 key 3 落在了 server 2 上,key 2 落在了 server 1 上。
step 5. 移除服务器
当 server 2 宕机了,受到影响的仅仅是圆环上 server 3 和 server 2 之间的数据(key 3 和 key 4),即映射到 server 2 的数据。
step 6. 添加服务器
如果要增加 server 4,通过映射,它将出现在 key 3 和 key 4 之间,则此时受到影响的将是 server 3 和 server 4 之间的数据(key 4)。把 key 4 重新映射到 server 4 上即可。