原标题:如何利用Redis锁解决高并发问题,看完就知道Redis是有多好用了
图书推荐
这里我们主要利用Redis的setnx的命令来处理高并发。
setnx 有两个参数。第一个参数表示键。第二个参数表示值。如果当前键不存在,那么会插入当前键,将第二个参数做为值。返回 1。如果当前键存在,那么会返回0。
创建库存表
CREATETABLE`storage`(
`id`int( 11) unsignedNOTNULLAUTO_INCREMENT,
`number`int( 11) DEFAULTNULL,
PRIMARY KEY( `id`)
) ENGINE= InnoDBAUTO_INCREMENT= 1DEFAULTCHARSET=latin1
设置初始库存为10
创建订单表
CREATETABLE`order`(
`id`int( 11) unsignedNOTNULLAUTO_INCREMENT,
`number`int( 11) DEFAULTNULL,
PRIMARY KEY( `id`)
) ENGINE= InnoDBAUTO_INCREMENT= 1DEFAULTCHARSET=latin1
测试不用锁的时候
$pdo = newPDO( '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|
+ ----+--------+
1row inset ( 0.00sec)
在来看订单表
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 inset (0.00 sec)
发现存在几个订单都是操作的同一个库存数据,这样就可能引起超卖的情况。
修改代码加入redis锁进行数据控制
/**
* Created by PhpStorm.
* User: daisc
* Date: 2018/7/23
* Time: 14:45
*/
classLock
{
privatestatic$_instance ;
private$_redis;
privatefunction__construct()
{
$this->_redis = newRedis();
$this->_redis ->connect('127.0.0.1');
}
publicstaticfunctiongetInstance()
{
if(self::$_instance instanceof self)
{
returnself::$_instance;
}
returnself::$_instance = newself();
}
/**
* @function加锁
* @param$key 锁名称
* @param$expTime 过期时间
*/
publicfunctionset($key,$expTime)
{
//初步加锁
$isLock = $this->_redis->setnx($key,time()+$expTime);
if($isLock)
{
returntrue;
}
else
{
//加锁失败的情况下。判断锁是否已经存在,如果锁存在切已经过期,那么删除锁。进行重新加锁
$val = $this->_redis->get($key);
if($val&&$val
{
$this->del($key);
}
return$this->_redis->setnx($key,time()+$expTime);
}
}
/**
* @param$key 解锁
*/
publicfunctiondel($key)
{
$this->_redis->del($key);
}
}
$pdo = newPDO('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 inset (0.00 sec)
发现订单表没有操作同一个库存数据的情况。所以利用redis锁是可以有效的处理高并发的。
这里在加锁的时候其实是可以不需要判断过期时间的,这里我们为了避免造成死锁,所以加一个过期时间的判断。当过期的时候主动删除该锁。返回搜狐,查看更多
责任编辑: