同時実行性が高いという問題はよく遭遇する問題ですが、同時実行性が高いという問題を解決するにはどうすればよいでしょうか?この記事では、同時実行性の問題を解決するための 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 サイトの他の関連記事を参照してください。