PHP 使用redis实现分布式锁

7 篇文章 0 订阅
7 篇文章 0 订阅

话不多说直接上代码

$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 (\yii\db\Exception $e) {
        $tr->rollBack();
        throw new Exception($e->getMessage());
    }
} finally {
    //释放锁
    $redisLock->release();
}

类Redis:

class Redis
{
    /**
     * @param string $db
     * @return \yii\redis\Connection
     */
    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\redis\Connection $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 = <<<LUA
if redis.call('get',KEYS[1]) == ARGV[1] then
    return redis.call('del',KEYS[1])
else
    return 0
end
LUA;
}

redis应用lua脚本案例https://blog.csdn.net/Gan_1314/article/details/125295143

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值