Maison  >  Article  >  développement back-end  >  Comment utiliser le verrouillage Redis pour résoudre des problèmes de concurrence élevée

Comment utiliser le verrouillage Redis pour résoudre des problèmes de concurrence élevée

不言
不言original
2018-09-10 14:45:543286parcourir

Le problème de la haute concurrence est un problème que nous rencontrons souvent, alors comment résoudre le problème de la haute concurrence ? Cet article présente l'utilisation des verrous Redis pour résoudre des problèmes de concurrence élevée. Jetons un coup d'œil.

Ici, nous utilisons principalement la commande setnx de Redis pour gérer une simultanéité élevée.

setnx a deux paramètres. Le premier paramètre représente la clé. Le deuxième paramètre représente la valeur. Si la clé actuelle n'existe pas, la clé actuelle sera insérée, en utilisant le deuxième paramètre comme valeur. Retour 1. Si la clé actuelle existe, 0 sera renvoyé.

Créer un tableau d'inventaire

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

Définir l'inventaire initial sur 10

Créer un tableau de commande

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

Test sans verrouillage

$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);
    }
}

Le test ab a simulé la concurrence et a révélé que l'inventaire était correct.

mysql> select * from storage;
+----+--------+
| id | number |
+----+--------+
|  1 |      0 |
+----+--------+
1 row in set (0.00 sec)

En regardant le tableau des commandes

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)

J'ai découvert que plusieurs commandes fonctionnent sur les mêmes données d'inventaire, ce qui peut entraîner des conditions de survente.

Modifiez le code et ajoutez un verrouillage Redis pour le contrôle des données

<?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
{
    //加锁不成功执行其他操作。
}

Effectuez à nouveau le ab test et vérifiez les résultats du test

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)

Il s'avère que la table de commande n'opère pas le même inventaire La situation des données. Par conséquent, l’utilisation du verrouillage Redis peut gérer efficacement une concurrence élevée.

En fait, il n'est pas nécessaire de juger le délai d'expiration lors du verrouillage. Ici, afin d'éviter une impasse, nous ajoutons un jugement sur le délai d'expiration. Supprimez activement le verrou lorsqu'il expire.

Recommandations associées :

php utilise des verrous de fichiers pour résoudre les problèmes de concurrence élevée

Que diriez-vous d'utiliser Redis pour stocker des sessions PHP Résoudre le problème de la concurrence et de la cohérence

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn