php使用redis实现分布式锁

常见的使用原因都是为了解决商品超卖导致的重复购买的问题#

什么是分布式锁#

分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,这个时候,便需要使用到分布式锁

实现原理#

实现分布式锁的原理很简单,就是需要有一把锁,多个服务同时去获取锁,但是只有一个服务能获取到锁。获取到锁的服务就可以执行自己的业务,没有获取到锁的其他服务需要等待获取到锁的服务业务执行完成后释放锁,然后再次尝试获取锁。

实现分布式的方案有很多种,如下#

  • 基于数据库实现分布式锁,比如mysql
  • 基于缓存实现分布式锁,比如redis
  • 基于Zookeeper实现分布式锁

    这里我们使用redis来实现分布式锁,在执行业务之前先获取一个key,如果key存在就说明已经有其他服务获得锁,这个时候需要等待或者返回系统繁忙。如果key不存在,说明没有其他服务获取锁,把这个key保存到redis,然后执行业务,等待业务执行完就从redis中删除这个key。

#

代码逻辑实现如下:#

 1. redis操作类分装#

<?php

namespace app\api;

class Redis extends \Redis
{
    public $redisSelf = null;

    public function __construct($db_select = 1)
    {
        if (!$this->redisSelf) {
            $con = new \Redis();
            $con->connect(env('redis.host'), env('redis.port'), 5);
            $con->auth(env('redis.password')); // 密码验证
            $con->select($db_select); // 选择数据库
            $this->redisSelf = $con;
        }
    }

    //获取锁
    public function getLock($key)
    {
        $value = $this->redisSelf->get($key);
        return $value;
    }

    //上锁
    public function setLock($key, $value, $time)
    {
        //设置锁
        $setNx = $this->redisSelf->setnx($key, $value);
        if (!$setNx) {
            return $setNx;
        }
        //设置锁的过期时间
        $expire = $this->redisSelf->expire($key, $time);
        if (!$expire) {
            $this->redisSelf->del($key);
        }
        return $expire;
    }

    //解锁
    public function delLock($key)
    {
        $releaseLock = $this->redisSelf->del($key);
        return $releaseLock;
    }

}

2. 请求操作类#

<?php

namespace app\api;

class Test
{
    protected $redis = null;

    function __construct()
    {
        if (!$this->redis) {
            $this->redis = new Redis();
        }
    }

    /**
     * @return false
     * @desc 利用redis分布式锁解决商品超卖【常见的解决方案】
     */
    public function sellMoreGoods()
    {
        $lockKey = 'carver_goods';
        $is_lock = $this->redis->getLock($lockKey);
        //查看当前的这个key是否已经加了redis锁,如果是加了,就不让进入操作了
        if (!$is_lock) {
            //已有锁,直接返回,不往下执行了
            return false;
        }
        //没有锁,加锁,业务开始加锁
        $expire_time = 10;
        $set_lock = $this->redis->setLock($lockKey, md5(time()), $expire_time);
        if (!$set_lock) {
            //加锁失败
            return false;
        }
        //业务开始
        $sellCount = $this->redis->get('sell_count');
        //销量小于库存1的时候可以卖出
        $kuCun = 1;
        if ($sellCount < $kuCun) {
            //模拟数据库阻塞和延迟
            sleep(5);
            //销量+1
            $this->redis->incr('sell_count');
        }
        //业务执行结束,解锁
        $this->redis->delLock($lockKey);
    }


}

 

作者:Carver-大脸猫

出处:https://www.cnblogs.com/carver/articles/17109454.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

转载请注明原处

posted @   Carver-大脸猫  阅读(265)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu