High concurrency is a problem we often encounter, so how can we solve this problem of high concurrency? This article describes the use of Redis locks to solve high concurrency problems, come together to see it.
Here we mainly use Redis's SETNX command to handle high concurrency.
The SETNX has two parameters. The first parameter represents a key. The second parameter represents a value. If the current key does not exist, the current key is inserted and the second parameter is a value. Returns 1. If the current key is present, 0 is returned.
Create an 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 Form
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
Testing when no locks are used
$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);
}
}
The AB test simulates concurrency and finds that the inventory is correct.
mysql> select * from storage;
+----+--------+
| id | number |
+----+--------+
| 1 | 0 |
+----+--------+
1 row in set (0.00 sec)
Look 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 that are the same inventory data for the operation, which can lead to oversold situations.
Modify code to join 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)
{
/ / Initial lock
$isLock = $this->_redis->setnx($key,time()+$expTime);
If($isLock)
{
Return true;
}
Else
{
//In case of locking failure. Determine if the lock already exists. If the latch has expired, delete the lock. Relock
$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();
/ / Judge is able to 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
{
//The lock does not succeed in performing other operations.
}
Test again toabsee the 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)
The Discovery Order table does not operate on the same inventory data condition. Therefore, the use of Redis locks can effectively handle high concurrency.
Here in the lock when you can actually do not need to determine the expiration time, here we to avoid causing deadlocks, so add an expiration time judgment. The lock is actively deleted when it expires.