文章正文

 

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

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锁进行数据控制
<?php /**  * Created by PhpStorm.  * User: daisc  * Date: 2018/7/23
 * Time: 14:45  */ class Lock {     private static $_instance ;
    private   $_redis;     private function __construct()     {
        $this->_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&&$val<time())
            {                 $this->del($key);             }
            return  $this->_redis->setnx($key,time()+$expTime);         }     }
    /**      * @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锁是可以有效的处理高并发的。

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

技术
今日推荐
下载桌面版
GitHub
百度网盘(提取码:draw)
Gitee
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:766591547
关注微信