PHP使用redis实现分布式锁

原创
小哥 2年前 (2023-05-16) 阅读数 552 #JAVA

话不多说直接上代码

$redisLock = Redis::lock(key, value, 5);//key使用商品id,value使用用户id拼接时间戳和随机字符
try {
    //获取锁:适用于n个人抢1个商品的场景,避免出现多个人抢成功。
    if (!$redisLock->acquire()) {
        throw new Exception(手慢了,已被领取);
    }
    //自旋锁:适用于n个人抢m个商品,避免出现超卖(即实际卖出数量大于m个)问题
    //参数1为获取锁的次数,参数2为获取锁的间隔时间。
    /*if (!$redisLock->spin(3, 0.5)) {
        throw new Exception(网络繁忙,请稍后再试);
    }*/

    $tr = DB::beginTransaction();
    try {
        //业务处理

        $tr->commit();
    } catch (yiidbException $e) {
        $tr->rollBack();
        throw new Exception($e->getMessage());
    }
} finally {
    //释放锁
    $redisLock->release();
}

类Redis:

class Redis
{
    /**
     * @param string $db
     * @return yii
    edisConnection
     */
    public static function connection(string $db = redis)
    {
        return app()->{$db};
    }

    /**
     * @param $name
     * @param $owner
     * @param int $seconds
     * @param string $db
     * @return Lock
     */
    public static function lock($name, $owner, int $seconds, string $db = redis)
    {
        return new Lock($name, $owner, $seconds, self::connection($db));
    }
}

类Lock:

class Lock
{
    private $key;

    private $value;

    private $seconds;

    private $redis;

    /**
     * RedisLock constructor.
     * @param $key
     * @param $value
     * @param int $seconds 过期秒数
     * @param yii
edisConnection $redis 指定的redis
     */
    public function __construct($key, $value, int $seconds, $redis)
    {
        $this->key = lock: . $key;
        $this->value = $value;
        $this->seconds = $seconds;
        $this->redis = $redis;
    }

    /**
     * 获取锁
     * @return bool
     */
    public function acquire()
    {
        if ($this->seconds) {
            return (bool)$this->redis->set($this->key, $this->value, ex, $this->seconds, nx);
        }
        return (bool)$this->redis->setnx($this->key, $this->value);
    }

    /**
     * 自旋锁
     * @param int $spinTimes 自旋次数
     * @param int|float $spinSeconds 间隔秒数
     * @return bool
     */
    public function spin(int $spinTimes = 2, $spinSeconds = 0.5)
    {
        if (is_float($spinSeconds)) {
            $microSeconds = intval(fmod($spinSeconds, 1) * 1000000);
            $spinSeconds = intval($spinSeconds);
        }
        while ($spinTimes > 0) {
            $lock = self::acquire();
            if ($lock || 0 == --$spinTimes) {
                return $lock;
            }
            if ($spinSeconds) {
                sleep($spinSeconds);
            }
            if (isset($microSeconds)) {
                usleep($microSeconds);
            }
        }
        return false;
    }

    /**
     * 释放锁
     * @return bool
     */
    public function release()
    {
        return (bool)$this->redis->eval(LuaScript::UNLOCK, 1, $this->key, $this->value);
    }
}

类LuaScript:

class LuaScript
{
    const UNLOCK = <<
版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除

热门