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);
    }

  • 相关阅读:
    SoftMax 回归(与Logistic 回归的联系与区别)
    OneNote转PDF
    数据库的模式分解相关算法规范步骤
    机器学习
    Java学习
    K-means 聚类
    Java学习
    [DBNETLIB][ConnectionOpen(connect()).]SQL Server 不存在或拒绝访问解决方方法
    sql语句技巧
    刚看到《SQL语法大全》、补全一下
  • 原文地址:https://www.cnblogs.com/myJuly/p/12640948.html
Copyright © 2011-2022 走看看