您的位置 首页 php

利用 Redis 锁解决高并发问题

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

创建库存表

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('my sql :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);
 }
 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 锁是可以有效的处理高并发的。

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

感谢作者: 旧梦发癫 ()

文章来源:智云一二三科技

文章标题:利用 Redis 锁解决高并发问题

文章地址:https://www.zhihuclub.com/150748.shtml

关于作者: 智云科技

热门文章

网站地图