zoukankan      html  css  js  c++  java
  • php+redis+lua实现分布式锁(转载)

    以下是我在工作中用到的类,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);
    }

  • 相关阅读:
    Java实现 LeetCode 56 合并区间
    JQuery实现对html结点的操作(创建,添加,删除)
    JQuery实现对html结点的操作(创建,添加,删除)
    JQuery实现对html结点的操作(创建,添加,删除)
    Java实现 LeetCode 55 跳跃游戏
    Java实现 LeetCode 55 跳跃游戏
    Java实现 LeetCode 55 跳跃游戏
    Java实现 LeetCode 54 螺旋矩阵
    Java实现 LeetCode 54 螺旋矩阵
    Java实现 LeetCode 54 螺旋矩阵
  • 原文地址:https://www.cnblogs.com/myJuly/p/12640948.html
Copyright © 2011-2022 走看看