Text
Here we mainly use Redis Of setnx To handle high concurrency .
setnx There are two parameters . The first parameter represents the key . The second parameter represents the value . If the current key does not exist , The current key is inserted , Take the second parameter as the value . return 1. If the current key exists , Then it will return 0.
Create inventory table
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
Set initial inventory to 10
Create order table
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
When testing without locks
$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 Test simulation concurrency , The inventory was found to be correct .
mysql> select * from storage; +----+--------+ | id | number | +----+--------+
| 1 | 0 | +----+--------+ 1 row in set (0.00 sec)
Looking at the order form
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)
It is found that there are several orders with the same inventory data , This may lead to oversold .
Modify code to add redis Lock for data control
<?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 Lock * @param $key Lock name * @param $expTime Expiration time */
public function set($key,$expTime) { // Preliminary locking
$isLock = $this->_redis->setnx($key,time()+$expTime);
if($isLock) { return true; } else
{ // In case of lock failure . Determine whether the lock already exists , If the lock exists, it has expired , Then delete the lock . Re lock
$val = $this->_redis->get($key); if($val&&$val<time())
{ $this->del($key); }
return $this->_redis->setnx($key,time()+$expTime); } }
/** * @param $key Unlock */ public function del($key) {
$this->_redis->del($key); } }
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'root');
$lockObj = Lock::getInstance(); // The judgment is that it can lock successfully
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); } } // Unlock
$lockObj->del('storage'); } else { // Locking failed to perform other operations . }
Again ab test , View test results
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)
It is found that the order table does not operate on the same inventory data . So use it redis Locks can effectively handle high concurrency .
When locking here, you don't need to judge the expiration time , In order to avoid a deadlock here , So add an expiration time judgment . Delete the lock when it is expired .
Technology