时间:2021-07-01 10:21:17 帮助过:18人阅读
这里我们主要利用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锁是可以有效的处理高并发的。
这里在加锁的时候其实是可以不需要判断过期时间的,这里我们为了避免造成死锁,所以加一个过期时间的判断。当过期的时候主动删除该锁。
相关推荐:
php利用文件锁来解决高并发问题
使用Redis来存储PHP的session如何解决并发一致性的问题
以上就是如何利用Redis锁解决高并发问题的详细内容,更多请关注Gxl网其它相关文章!