日度归档:2012年3月27日

memcache一致性hash的php实现

有段时间没认真写博客了,最近在看一些分布式方面的文章,所以就用php实现一致性hash来练练手,以前一般用的是最原始的hash取模做分布式,当生产过程中添加或删除一台memcache都会造成数据的全部失效,一致性hash就是为了解决这个问题,把失效数据降到最低,相关资料可以google一下!

php实现效率有一定的缺失,如果要高效率,还是写扩展比较好
经测试,5个memcache,每个memcache生成200个虚拟节点,set加get1000次,采用一致性哈希分布效率比原生单台速度相差5倍,效率有待优化
实现过程:

  1. memcache的配置 ip+端口+虚拟节点序列号 做hash,使用的是crc32,形成一个闭环。
  2. 对要操作的key进行crc32
  3. 二分法在虚拟节点环中查找最近的一个虚拟节点
  4. 从虚拟节点中提取真实的memcache ip和端口,做单例连接

代码如下: 继续阅读

redis做共享锁机制

给出部分主代码

感谢HaKeem的提醒,程序发现一个bug,以前的代码会发生无法解锁的问题,放出修正后的代码
如果是redis2.2以上版本可以在lock逻辑中加上watch命令,锁定单个key

	/**
	 * 加锁  【新的加锁算法,采用redis做锁,redis失效情况下,返回 变量redisErrReturn 设定的值】
	 * @param string $key 唯一标识
	 * @param int $expire
	 * @return
	 */
	public function lock($key, $expire = 5) {
		try {
			list($key, $lockIdKey) = $this->_getLockKey($key);
			//抢锁,第一个线程抢到,并把过期时间写入锁
			if (Common::getQueue()->setnx($key, Common::getTime() + $expire)) return true;
			//没有抢到锁的线程,判断锁是否异常死锁,如果锁没有过期,返回false
			if (Common::getQueue()->get($key)  > Common::getTime()) return false;
			//锁因异常死锁并过期的情况下,多个并发线程再次抢锁,getset命令到过期时间,如果未过期,表示锁已被其它线程抢得,返回false
			if (Common::getQueue()->getset($key, Common::getTime() + $expire) > Common::getTime()) return false;
			Common::getQueue()->set($lockIdKey, $this->_lockId);
			return true;
		} catch (RedisException $e) {
			// 当捕捉到redis异常时,锁中断,返回变量redisErrReturn
			$this->_lockStatus = false;
			return $this->redisErrReturn;
		}
	}
	
	/**
	 * 给锁进行续期,延长锁的生效周期
	 * @param string $key
	 * @param int $expire       延时锁失效的秒数
	 * @param int $triggerTime  触发续期倒计时间
	 * @return
	 */
	public function updateExpire($key, $expire = 3, $triggerTime = 2) {
		if (!$this->_lockStatus) return true; //当redis失效,中断锁
		list($key, $lockIdKey) = $this->_getLockKey($key);
		$lockId = Common::getQueue()->get($lockIdKey);
		if ($this->_lockId === null || $lockId != $this->_lockId) return false;
		$time = Common::getQueue()->get($key);
		if ($time - $triggerTime <= Common::getTime()) return Common::getQueue()->set($key, $time + $expire);
	}
	
	/**
	 * 解锁
	 * @param string $key
	 * @return
	 */
	public function unlock($key) {
		if (!$this->_lockStatus) return true; //当redis失效,中断解锁
		list($key, $lockIdKey) = $this->_getLockKey($key);
		$lockId = Common::getQueue()->get($lockIdKey);
		if ($this->_lockId === null || $lockId != $this->_lockId) return false;
		Common::getQueue()->del($key);
		Common::getQueue()->del($lockIdKey);
		return true;
	}
	
	/**
	 * 清除死锁产生的无用key 默认清除24小时以前的无用key
	 * @param int $cleanTime  默认24小时
	 */
	public function cleanLockKey($cleanTime = 86400) {
		if ($cleanTime < 3600 || !$keys = Common::getQueue()->keys('PwLock:*')) return false;
		$i = 0;
		foreach ($keys as $value) {
			list($time, $lockId) = explode('|', Common::getQueue()->get($value));
			if (Common::getTime() - $time > $cleanTime) {
				if (Common::getQueue()->del($value)) $i++;
			}
		}
		return $i;
	}
	
	private function _getLockKey($key) {
		return array('PwLock:' . $key, 'PwLock:' . $key . ':lockId');
	}