ここでは、使用して、高同時実行に対処するためのコマンドを。問題は、期限切れにSETNXで、ある優れた原子を操作する必要がNXセットパラメータに設定し、リアロック操作を使用して、より良い方法があり、時間を設定する必要がないので、それはもはや原子でありますRedis
setnx
setnx
2つのパラメータがあります。最初のパラメータはキーを表します。第2のパラメータは、値を示します。現在のキーが存在しない場合は、2番目のパラメータの値として、現在のキーを挿入します。リターン1
。現在のキーが存在する場合、それが返されます0
。
在庫表の作成
(TABLE `storage`をCREATE `id` int型( 11)符号なしNOT NULL AUTO_INCREMENT、 ` 番号 `int型(11)DEFAULTの NULL、 PRIMARY KEY (` id`) )ENGINE = InnoDBのAUTO_INCREMENT = 1 DEFAULT CHARSET = LATIN1
10の初期在庫を設定します。
オーダー表を作成します。
(TABLE `order`をCREATE `id` int型( 11)符号なしNOT NULL AUTO_INCREMENT、 ` 番号 `int型(11)DEFAULTの NULL、 PRIMARY KEY (` id`) )ENGINE = InnoDBのAUTO_INCREMENT = 1 DEFAULT CHARSET = LATIN1
テストロックしないでください。
$のpdo =新しいPDO( 'mysqlの:ホスト= 127.0.0.1; DBNAME =テスト'、 'ルート'、 'ルート'); $のSQLは= "` ID = 1つのリミット1ストレージからnumber`選択" と、 $ RES = $ PDO->クエリ($ sqlを) - >フェッチ(); $番号= $のres [ '数']; もし($番号> 0) { $のSQLは= "` order`値(NULL、$番号)に挿入します"; $ ORDER_ID = $ PDO->クエリ($ sqlを); ($ ORDER_ID)の場合 { $のSQL = "更新ストレージ設定` number` = `number`-1、ID = 1"。 $ PDO->クエリ($ sqlを); } }
ABテストは、同時見つけ在庫が正しいことをシミュレートします。
mysqlの > *を選択し、ストレージから。 + ---- + -------- + | ID | 数 | + ---- + -------- + | 1 | 0 | + ---- + -------- + セット内の1行(0.00秒)
Ordersテーブルのビューで
MySQLの> `order`から選択*; + ---- + -------- + | ID | 数| + ---- + -------- + | 1 | 10 | | 2 | 10 | | 3 | 9 | | 4 | 7 | | 5 | 6 | | 6 | 5 | | 7 | 5 | | 8 | 5 | | 9 | 4 | | 10 | 1 | + ---- + -------- + セット内の10行(0.00秒)
私たちは、同じインベントリデータは、操作の数桁で見つかったので、状況が売られ過ぎの原因となります。
コードを修正しredis
制御するためのロックデータ
<?PHP / * * * PhpStormによって作成されます。 *ユーザー:daisc *日:2018年7月23日 *時間:14時45分 * / クラスのロック { プライベート 静的 $の_INSTANCE 。 民間 の$ _redis 。 プライベート 関数__construct() { $この - > _ Redisの= 新しいRedisの(); $この - > _ Redisの- >( '127.0.0.1'を接続します)。 } 公共の 静的な 機能のgetInstance() { もし(自己:: $の_INSTANCE 自己のinstanceof) { 返す自己:: $ _INSTANCE 。 } 返す ::自己$ _INSTANCE = 新しい 自己()を。 } / * * * @関数ロック * @Param $キーロック名 * @Param $ EXPTIME有効期限 * / パブリック 関数セット($キー、$ EXPTIME ) { // 予備ロック $ isLock = $この - > _ redis-> SETNX($キー、時間()+ $ EXPTIME ); IF($ isLock ) { 返す 真; } 他 { // 失敗した場合にロック。カットの有効期限が切れているロックがあればロックがすでに存在するかどうかを判断し、その後、ロックを外します。再ロック $ヴァル = $この - > _ redis-> GET($キー); IF($ヴァル && $ヴァル < 時間()) { $この - >デル($キー)。 } 返す $この - > _ redis-> setnx($キー、時間()+ $ EXPTIME )。 } } / * * * @Paramの$キーロックを解除するには * / 公共の 機能デル($キー) { $この - > _ redis->デル($キー)。 } } PDO $ = 新しい新しい PDO( 'MySQLの:ホスト= 127.0.0.1; dbnameは=テスト'、 'ルート'、 'ルート' ); $ lockObjは = ::ロックのgetInstanceを(); // ロックを分析することが可能であるに成功した IF($ロック = $ lockObj - > SET( '保存'、10 )) { $のSQLは = "` ID = 1つのリミット1ストレージからnumber`選択"を、 $ RES = $ PDO - >クエリ($ sqlを) - > (フェッチ)。 $番号 = $のres [ '番号' ]; もし($番号 > 0 ) { $のSQLは = "` order`値(NULL、挿入$番号)" 。 $ ORDER_ID = $ PDO - >クエリ($ sqlを); もし($ ORDER_ID ) { $のSQL = "更新ストレージ設定` number` = `number`-1、ID = 1" 。 $のPDO - >クエリ($ sqlを); } } //は、ロックを解除 $をlockObj - >デル( 'ストレージ' ); } 他 { // 失敗したロックその他の操作を実行します。 }
ここでもab
、テスト、テスト結果を表示
MySQLの> `order`から選択*; + ---- + -------- + | ID | 数| + ---- + -------- + | 1 | 10 | | 2 | 9 | | 3 | 8 | | 4 | 7 | | 5 | 6 | | 6 | 5 | | 7 | 4 | | 8 | 3 | | 9 | 2 | | 10 | 1 | + ---- + -------- + セット内の10行(0.00秒)
Ordersテーブルには、同じ動作条件のインベントリデータを見つけることができません。したがって、使用のredis
ロックが有効に高度に並行処理することができます。
実際には、ここでは、デッドロック、その裁判官の有効期限の追加を避けるために、ここで、有効期限は、ロック時に決定する必要はないかもしれないです。イニシアチブの有効期限は、ロックを削除するとき。