这里我们主要利用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锁是可以有效的处理高并发的。
这里在加锁的时候其实是可以不需要判断过期时间的,这里我们为了避免造成死锁,所以加一个过期时间的判断。当过期的时候主动删除该锁。
相关推荐:
使用Redis来存储PHP的session如何解决并发一致性的问题
以上就是如何利用Redis锁解决高并发问题的详细内容,更多请关注php中文网其它相关文章!
网站建设是一个广义的术语,涵盖了许多不同的技能和学科中所使用的生产和维护的网站。
……