Redis ロックを使用して同時実行性の高い問題を解決する方法

不言
リリース: 2023-04-04 06:46:02
オリジナル
3362 人が閲覧しました

同時実行性が高いという問題はよく遭遇する問題ですが、同時実行性が高いという問題を解決するにはどうすればよいでしょうか?この記事では、同時実行性の問題を解決するための Redis ロックの使用法を紹介します。

ここでは主に Redis の setnx コマンドを使用して高い同時実行性を処理します。

setnx には 2 つのパラメータがあります。最初のパラメータはキーを表します。 2 番目のパラメータは値を表します。現在のキーが存在しない場合は、2 番目のパラメータを値として使用して、現在のキーが挿入されます。 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 セッションを保存する方法同時実行性と一貫性の問題を解決する

以上がRedis ロックを使用して同時実行性の高い問題を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート