给出部分主代码
感谢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'); }
LZ,redis共享锁有点问题,看下这篇文章
http://huanLgz.iteye.com/blog/1381538
有知道有这么个问题,也想过加watch,可是我们这边的生产环境的redis版本是2.0的,不支持watch命令。至于这个bug,高并发测试下并没有锁突破的问题,所以在redis升级之前只能先用着了!