资讯专栏INFORMATION COLUMN

利用Redis锁解决高并发问题

bitkylin / 1072人阅读

摘要:创建库存表设置初始库存为创建订单表测试不用锁的时候测试模拟并发,发现库存是正确的。再次进行测试,查看测试结果发现订单表没有操作同一个库存数据的情况。所以利用锁是可以有效的处理高并发的。当过期的时候主动删除该锁。

这里我们主要利用Redissetnx的命令来处理高并发。

setnx 有两个参数。第一个参数表示键。第二个参数表示值。如果当前键不存在,那么会插入当前键,将第二个参数做为值。返回 1。如果当前键存在,那么会返回0

创建库存表

CREATE TABLE `storage` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `number` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

设置初始库存为10

创建订单表

CREATE TABLE `order` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `number` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

测试不用锁的时候

$pdo = new PDO("mysql:host=127.0.0.1;dbname=test", "root", "root");
$sql="select `number` from  storage where id=1 limit 1";
$res = $pdo->query($sql)->fetch();
$number = $res["number"];
if($number>0)
{
    $sql ="insert into `order`  VALUES (null,$number)";

    $order_id = $pdo->query($sql);
    if($order_id)
    {

        $sql="update storage set `number`=`number`-1 WHERE id=1";
        $pdo->query($sql);
    }
}

ab测试模拟并发,发现库存是正确的。

mysql> select * from storage;
+----+--------+
| id | number |
+----+--------+
|  1 |      0 |
+----+--------+
1 row in set (0.00 sec)

在来看订单表

mysql> select * from `order`;
+----+--------+
| id | number |
+----+--------+
|  1 |     10 |
|  2 |     10 |
|  3 |      9 |
|  4 |      7 |
|  5 |      6 |
|  6 |      5 |
|  7 |      5 |
|  8 |      5 |
|  9 |      4 |
| 10 |      1 |
+----+--------+
10 rows in set (0.00 sec)

发现存在几个订单都是操作的同一个库存数据,这样就可能引起超卖的情况。

修改代码加入redis锁进行数据控制

_redis =  new Redis();
        $this->_redis ->connect("127.0.0.1");
    }
    public static function getInstance()
    {
        if(self::$_instance instanceof self)
        {
            return self::$_instance;
        }
        return self::$_instance = new  self();
    }

    /**
     * @function 加锁
     * @param $key 锁名称
     * @param $expTime 过期时间
      */
    public function set($key,$expTime)
    {
        //初步加锁
        $isLock = $this->_redis->setnx($key,time()+$expTime);
        if($isLock)
        {
            return true;
        }
        else
        {
            //加锁失败的情况下。判断锁是否已经存在,如果锁存在切已经过期,那么删除锁。进行重新加锁
            $val = $this->_redis->get($key);
            if($val&&$valdel($key);
                return  $this->_redis->setnx($key,time()+$expTime);
            }
            return false;
        }
    }


    /**
     * @param $key 解锁
     */
    public function del($key)
    {
        $this->_redis->del($key);
    }

}



$pdo = new PDO("mysql:host=127.0.0.1;dbname=test", "root", "root");
$lockObj = Lock::getInstance();
//判断是能加锁成功
if($lock = $lockObj->set("storage",10))
{
    $sql="select `number` from  storage where id=1 limit 1";
    $res = $pdo->query($sql)->fetch();
    $number = $res["number"];
    if($number>0)
    {
        $sql ="insert into `order`  VALUES (null,$number)";

        $order_id = $pdo->query($sql);
        if($order_id)
        {

            $sql="update storage set `number`=`number`-1 WHERE id=1";
            $pdo->query($sql);
        }
    }
    //解锁
    $lockObj->del("storage");

}
else
{
    //加锁不成功执行其他操作。
}

再次进行ab测试,查看测试结果

mysql> select * from `order`;
+----+--------+
| id | number |
+----+--------+
|  1 |     10 |
|  2 |      9 |
|  3 |      8 |
|  4 |      7 |
|  5 |      6 |
|  6 |      5 |
|  7 |      4 |
|  8 |      3 |
|  9 |      2 |
| 10 |      1 |
+----+--------+
10 rows in set (0.00 sec)

发现订单表没有操作同一个库存数据的情况。所以利用redis锁是可以有效的处理高并发的。

这里在加锁的时候其实是可以不需要判断过期时间的,这里我们为了避免造成死锁,所以加一个过期时间的判断。当过期的时候主动删除该锁。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/29398.html

相关文章

  • 利用Redis解决并发问题

    摘要:创建库存表设置初始库存为创建订单表测试不用锁的时候测试模拟并发,发现库存是正确的。再次进行测试,查看测试结果发现订单表没有操作同一个库存数据的情况。所以利用锁是可以有效的处理高并发的。当过期的时候主动删除该锁。 这里我们主要利用Redis的setnx的命令来处理高并发。 setnx 有两个参数。第一个参数表示键。第二个参数表示值。如果当前键不存在,那么会插入当前键,将第二个参数做为值...

    gecko23 评论0 收藏0
  • Redis分布式解决抢购问题

    摘要:废话不多说,首先分享一个业务场景抢购。下面就是分布式锁的解决方法。首先要加入的依赖,该类只有两个功能,加锁和解锁,解锁比较简单,就是删除当前的键值对。这时继续执行,由于所以该线程获取到锁。 废话不多说,首先分享一个业务场景-抢购。一个典型的高并发问题,所需的最关键字段就是库存,在高并发的情况下每次都去数据库查询显然是不合适的,因此把库存信息存入Redis中,利用redis的锁机制来控制...

    taoszu 评论0 收藏0
  • 秒杀系统优化方案之缓存、队列、设计思路

    摘要:一为什么难秒杀系统难做的原因库存只有一份,所有人会在集中的时间读和写这些数据。又例如抢票,亦与秒杀类似,瞬时流量更甚。 一、为什么难     秒杀系统难做的原因:库存只有一份,所有人会在集中的时间读和写这些数据。例如小米手机每周二的秒杀,可能手机只有1万部,但瞬时进入的流量可能是几百几千万。又例如12306抢票,亦与秒杀类似,瞬时流量更甚。 主要需要解决的问题有两个: 高并发对数据库...

    dinfer 评论0 收藏0
  • 秒杀系统优化方案之缓存、队列、设计思路

    摘要:一为什么难秒杀系统难做的原因库存只有一份,所有人会在集中的时间读和写这些数据。又例如抢票,亦与秒杀类似,瞬时流量更甚。 一、为什么难     秒杀系统难做的原因:库存只有一份,所有人会在集中的时间读和写这些数据。例如小米手机每周二的秒杀,可能手机只有1万部,但瞬时进入的流量可能是几百几千万。又例如12306抢票,亦与秒杀类似,瞬时流量更甚。 主要需要解决的问题有两个: 高并发对数据库...

    Meathill 评论0 收藏0
  • 分布式之redis复习精讲

    摘要:引言为什么写这篇文章博主的分布式之消息队列复习精讲得到了大家的好评,内心诚惶诚恐,想着再出一篇关于复习精讲的文章。单线程的为什么这么快分析这个问题其实是对内部机制的一个考察。 引言 为什么写这篇文章? 博主的《分布式之消息队列复习精讲》得到了大家的好评,内心诚惶诚恐,想着再出一篇关于复习精讲的文章。但是还是要说明一下,复习精讲的文章偏面试准备,真正在开发过程中,还是脚踏实地,一步一个脚...

    cooxer 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<