• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

php+redis+lua实现分布式锁(转载)

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

以下是我在工作中用到的类,redis加锁两种方式,解锁为了保证原子性所以只用lua+redis的方式

缺陷:虽然死锁问题解决了,但业务执行时间超过锁有效期还是存在多客户端加锁问题。
不过,这个类已经满足了我现在的业务需求

更优的解决方案可以参考以下两篇文章:
https://redis.io/topics/distlock (Redlock的算法描述)
https://mp.weixin.qq.com/s/1bPLk_VZhZ0QYNZS8LkviA

代码实现:

class RedisLock
{
/**
* @var 当前锁标识,用于解锁
*/
private $_lockFlag;

private $_redis;

public function __construct($host = \'127.0.0.1\', $port = \'6379\', $passwd = \'\')
{
$this->_redis = new Redis();
$this->_redis->connect($host, $port);
if ($passwd) {
$this->_redis->auth($passwd);
}
}

public function lock($key, $expire = 5)
{
$now= time();
$expireTime = $expire + $now;
if ($this->_redis->setnx($key, $expireTime)) {
$this->_lockFlag = $expireTime;
return true;
}

// 获取上一个锁的到期时间
$currentLockTime = $this->_redis->get($key);
if ($currentLockTime < $now) {
/* 用于解决
C0超时了,还持有锁,加入C1/C2/...同时请求进入了方法里面
C1/C2都执行了getset方法(由于getset方法的原子性,
所以两个请求返回的值必定不相等保证了C1/C2只有一个获取了锁) */
$oldLockTime = $this->_redis->getset($key, $expireTime);
if ($currentLockTime == $oldLockTime) {
$this->_lockFlag = $expireTime;
return true;
}
}

return false;
}

public function lockByLua($key, $expire = 5)
{
$script = <<<EOF

local key = KEYS[1]
local value = ARGV[1]
local ttl = ARGV[2]

if (redis.call(\'setnx\', key, value) == 1) then
return redis.call(\'expire\', key, ttl)
elseif (redis.call(\'ttl\', key) == -1) then
return redis.call(\'expire\', key, ttl)
end

return 0
EOF;

$this->_lockFlag = md5(microtime(true));
return $this->_eval($script, [$key, $this->_lockFlag, $expire]);
}

public function unlock($key)
{
$script = <<<EOF

local key = KEYS[1]
local value = ARGV[1]

if (redis.call(\'exists\', key) == 1 and redis.call(\'get\', key) == value)
then
return redis.call(\'del\', key)
end

return 0

EOF;

if ($this->_lockFlag) {
return $this->_eval($script, [$key, $this->_lockFlag]);
}
}

private function _eval($script, array $params, $keyNum = 1)
{
$hash = $this->_redis->script(\'load\', $script);
return $this->_redis->evalSha($hash, $params, $keyNum);
}

}

$redisLock = new RedisLock();

$key = \'lock\';
if ($redisLock->lockByLua($key)) {
// to do...
$redisLock->unlock($key);
}


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
lua 5.3 英文手册 google机器翻译版发布时间:2022-07-22
下一篇:
LUA TABLE 函数库(一)——理论发布时间:2022-07-22
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap