Memcached分布式布置方案

有时一台Memcached服务器不能满足我们的需求,需要布置多台Memcached服务器。但是有个问题,怎么确定一个数据应该保存到哪台服务器上面呢?

有两种方案,第一种是普通的Hash分布,第二种是一致性Hash分布。下面通过PHP作为客户端来说明这两种方案。


普通Hash分布

function toHash($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;
}

首先是通过md5把key处理成一个32位字符串,取其前8字符。再经过Hash算法处理成一个整数并返回,然后映射到其中一台Memcached服务器。

假设有两台Memcached服务器,可以使用下面的代码映射:

$servers = array(
	array('host' => '192.168.1.1', 'port' => 1121),
	array('host' => '192.168.1.2', 'port' => 1122)
);
$cache_key = 'cache_key';
$cache_val = 'cache_val';
$sc = $servers[toHash($cache_key) % 2];
$memcached = new Memcached($sc);
$memcached->set($cache_key, $value);
通过Hash函数把cache_key转化成整数后,利用这个整数与Memcached服务器的数量取模,这样就得到的是其中一台服务器的配置,利用这个配置连接Memcached服务器。这样就完成了分布式布置。取数据跟保存数据的方法一样。


一致性Hash分布

在服务器数量不发生改变的时候,普通的Hash分布可以很好地动作。当服务器数量发生变化时,问题就出来了。试想,增加一台服务器时,同一个key经过Hash之后,与服务器数取模的结果跟没增加之前的结果会不一样,这就导致之前保存的数据丢失。为了把丢失的数据减到最少,可以采取一致性Hash ( Consistent Hashing )分布算法解决。

一致性Hash分布算法分4个步骤,如下所示。

步骤1    将一个32位整数 ( 即 0 ~2^32 -1 )想象成一个环,当然这只是想象,将0作为圆环的头,2^32 -1 作为圆环的尾,把它连接起来,就成为一个环。

步骤2    通过toHash函数把key处理成整数。例如:

$key1 = toHash('key1');
$key2 = toHash('key2');
把key处理成整数后,就可以在环中找到一个位置与之对应。

步骤3    把Memcached群映射到环上,使用Hash函数处理服务器所使用的IP地址。例如有三台服务器:

$server1 = toHash('192.168.1.1');
$server2 = toHash('192.168.1.2');
$server3 = toHash('192.168.1.3');
经过上面几个步骤,我们把数据的key和服务器都映射到同一个不环上。下面考虑如何把数据映射到服务器上。

步骤4    把数据映射到服务器上,沿着圆环顺时针方向的key出发,直到遇到一个服务器为止,把key对应的数据保存到这个服务器上。


如果现在server2服务器崩溃了,那么受到影响的仅是那些沿着server2逆时针出发直到遇到下一个服务器server3之间的数据,也就是映射到server2上的那些数据。

如果现在需要增加一台服务器server4,例如增加之后server4在server3与server1之间,那部分原本映射到server1上面的数据现在会映射到server4上面。


经过这种一致性hash方式,在增加或者减少服务器的时候,只会改变很少一部分数据的存储服务器,从而减少了数据丢失的情况。


//多台缓存服务器一致性Hash分布算法实例
class FlexiHash{
	private $serverList = array();
	private $isSorted = false;
	
	/**
	 * @des Hash方法,首先通过md5把key处理成一个32位字符串,取其前8字符,再经过Hash算法处理成一个整数并返回,然后映射到其中一台缓存服务器。
	 * @param string $key 键值
	 * @return int 
	*/
	
	public function toHash($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;
	}
	
	//添加服务器
	/**
	 *
	 *首先通过toHash函数计算出服务器的Hash值,
	  通过此Hash值定位服务器到列表上的某个位置。
	  因为此时服务器列表发生了变化,所以应该把排序标识$isSorted设置为false。 
	 *
	 */
	public function addServer($server){
		$hash = $this->toHash($server);
		if(!isset($this->serverList[$hash])){
			$this->serverList[$hash] = $server;	
		}
		$this->isSorted = false;
		return true;
	}
	
	//移除服务器
	/**
	 *首先通过toHash函数计算出服务器的Hash值,
	  以此Hash值作为要删除服务器的索引,
	  把此服务器从服务器列表中删除。
	  因为此时服务器列表发生了变化,所以把排序标识$isSorted设置为falsh。
	 *
	 */
	public function removeServer($server){
		$hash = $this->toHash($server);
		if(isset($this->serverList[$hash])){
			unset($this->serverList[$hash]);	
		}
		$this->isSorted = false;
		return true;
	}
	
	//找到合适的服务器返回
	/**
	 *首先通过toHash函数计算出key的Hash值,然后判断服务器列表是否排过序,
	  如果没有,就先对服务器列表进行倒序排序操作。
	  倒序排序的作用是把服务器列表转化成一个逆时针的圆环。
	  最后遍历服务器列表,找到一个合适的服务器返回。
	 *
	 */
	public function lookup($key){
		$hash = $this->toHash($key);
		if(!$this->isSorted){
			krsort($this->serverList, SORT_NUMERIC);
			$this->isSorted = true;	
		}	
		foreach($this->serverList as $pos => $server){
			if($hash >= $pos){
				return $server;	
			}	
		}
		return $this->serverList[count($this->serverList) - 1];
	}	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值