方案1

class Lock
{
    private $redis='';  #存储redis对象

    public function __construct($host,$port=6379)
    {
        $this->redis=new Redis();
        $this->redis->connect($host,$port);
    } 

    /**
    * @desc 加锁方法
    *
    * @param $lockName string | 锁的名字
    * @param $timeout int | 锁的过期时间
    *
    * @return 成功返回identifier/失败返回false
    */
    public function getLock($lockName, $timeout=2)
    {
        $identifier=uniqid();       #获取唯一标识符
        $timeout=ceil($timeout);    #确保是整数
        $end=time()+$timeout;
        while(time()<$end)          #循环获取锁
        {
            if($this->redis->setnx($lockName, $identifier))    #查看$lockName是否被上锁
            {
                $this->redis->expire($lockName, $timeout);     #为$lockName设置过期时间,防止死锁
                return $identifier;                             #返回一维标识符
            } elseif ($this->redis->ttl($lockName)===-1) {                               
                $this->redis->expire($lockName, $timeout);     #检测是否有设置过期时间,没有则加上(假设,客户端A上一步没能设置时间就进程奔溃了,客户端B就可检测出来,并设置时间)
            }
            usleep(0.001);         #停止0.001ms
        }
        return false;
    }

    /**
    * @desc 释放锁
    *
    * @param $lockName string   | 锁名
    * @param $identifier string | 锁的唯一值
    *
    * @param bool
    */
    public function releaseLock($lockName,$identifier)
    {
        if($this->redis->get($lockName)==$identifier)   #判断是锁有没有被其他客户端修改
        { 
            $this->redis->multi();
            $this->redis->del($lockName);   #释放锁
            $this->redis->exec();
            return true;
        }
        else
        {
            return false;   #其他客户端修改了锁,不能删除别人的锁
        }
    }

    /**
    * @desc 测试
    * 
    * @param $lockName string | 锁名
    */
    public function test($lockName)
    {
        $start=time();
        for ($i=0; $i < 10000; $i++) 
        { 
            $identifier=$this->getLock($lockName);
            if($identifier)
            {
              $count=$this->redis->get('count');
              $count=$count+1;
              $this->redis->set('count',$count);
              $this->releaseLock($lockName,$identifier);
            } 
        }
        $end=time();
        echo "this OK<br/>";
        echo "执行时间为:".($end-$start);
    }
}

方案2

class RedisMutexLock
{
    /**
     * 缓存 Redis 连接。
     *
     * @return void
     */
    public static function getRedis()
    {
        // 这行代码请根据自己项目替换为自己的获取 Redis 连接。
        return YCache::getRedisClient();
    }

    /**
     * 获得锁,如果锁被占用,阻塞,直到获得锁或者超时。
     * -- 1、如果 $timeout 参数为 0,则立即返回锁。
     * -- 2、建议 timeout 设置为 0,避免 redis 因为阻塞导致性能下降。请根据实际需求进行设置。
     *
     * @param  string  $key         缓存KEY。
     * @param  int     $timeout     取锁超时时间。单位(秒)。等于0,如果当前锁被占用,则立即返回失败。如果大于0,则反复尝试获取锁直到达到该超时时间。
     * @param  int     $lockSecond  锁定时间。单位(秒)。
     * @param  int     $sleep       取锁间隔时间。单位(微秒)。当锁为占用状态时。每隔多久尝试去取锁。默认 0.1 秒一次取锁。
     * @return bool 成功:true、失败:false
     */
    public static function lock($key, $timeout = 0, $lockSecond = 20, $sleep = 100000)
    {
        if (strlen($key) === 0) {
            // 请更换为自己项目抛异常的方法。
            YCore::exception(500, '缓存KEY没有设置');
        }
        $start = self::getMicroTime();
        $redis = self::getRedis();
        do {
            // [1] 锁的 KEY 不存在时设置其值并把过期时间设置为指定的时间。锁的值并不重要。重要的是利用 Redis 的特性。
            $acquired = $redis->set("Lock:{$key}", 1, ['NX', 'EX' => $lockSecond]);
            if ($acquired) {
                break;
            }
            if ($timeout === 0) {
                break;
            }
            usleep($sleep);
        } while (!is_numeric($timeout) || (self::getMicroTime()) < ($start + ($timeout * 1000000)));
        return $acquired ? true : false;
    }

    /**
     * 释放锁
     *
     * @param  mixed  $key  被加锁的KEY。
     * @return void
     */
    public static function release($key)
    {
        if (strlen($key) === 0) {
            // 请更换为自己项目抛异常的方法。
            YCore::exception(500, '缓存KEY没有设置');
        }
        $redis = self::getRedis();
        $redis->del("Lock:{$key}");
    }

    /**
     * 获取当前微秒。
     *
     * @return bigint
     */
    protected static function getMicroTime()
    {
        return bcmul(microtime(true), 1000000);
    }
}

results matching ""

    No results matching ""