php redis分布式互斥锁(基于lua脚本保证原子性)

  • Post author:
  • Post category:php


<?php
/**
 * Created by PhpStorm.
 * User: Hank
 * Date: 2019/3/25 0025
 * Time: 下午 15:14
 */

namespace App\Lib\Redis;


use Predis\ClientInterface;

/**
 * redis锁
 * 安全和可靠性保证
    在描述我们的设计之前,我们想先提出三个属性,这三个属性在我们看来,是实现高效分布式锁的基础。
    一致性:互斥,不管任何时候,只有一个客户端能持有同一个锁。
    分区可容忍性:不会死锁,最终一定会得到锁,就算一个持有锁的客户端宕掉或者发生网络分区。
    可用性:只要大多数Redis节点正常工作,客户端应该都能获取和释放锁。
 * Class RedisLock
 * @author hank
 * @package App\Lib\Redis
 */
class RedisLock
{
    const LOCK_REDIS_KEY_PREFIX = "LOCK_KEY:";
    /**
     * @var ClientInterface
     */
    private $redis;

    /**
     * RedisLock constructor.
     * @param ClientInterface $redis
     */
    public function __construct(ClientInterface $redis)
    {
        $this->redis = $redis;
    }

    /**
     * 获得锁
     * @param $key
     * @param $timeout int 超时秒数默认10秒
     * @return bool|int  获得成功锁返回随机value
     */
    public function lock($key, $timeout = 10){
        $myRandomValue = mt_rand();
        $key = self::LOCK_REDIS_KEY_PREFIX.$key;
        if($this->redis->eval("return redis.call('SET', KEYS[1], KEYS[2], 'NX', 'PX', KEYS[3])", 3, $key, $myRandomValue, $timeout*1000)){
            return $myRandomValue;
        }
        return false;
    }

    /**
     * 解锁
     * !这里如果锁自动过期不存在了也返回 解锁成功true!
     * @param $key
     * @param $myRandomValue int 解锁随机值
     * @return bool
     */
    public function unLock($key, $myRandomValue){
        $key = self::LOCK_REDIS_KEY_PREFIX.$key;
        if($this->redis->eval("if redis.call('EXISTS',KEYS[1]) == 0 then return 1 else if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end end", 1, $key, $myRandomValue)){
            return true;
        }
        return false;
    }
}

这里需要明白为什么使用lua脚本,因为redis的lua脚本是

原子性(事务)的

,加锁解锁整个过程必须保证原子性(事务),具体的原理可以参考:

《Redis官方文档》用Redis构建分布式锁



版权声明:本文为huang007guo原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。