1. 同時実行性の問題
ここで話しているのは、同じリソースを取得するための複数の同時リクエストです
リクエスト:index.php?mod=a&action=b&taskid=6 処理:
rreee2. 分析
ロジックは問題ないようですが、記録されたタイムスタンプを調べてみると、なんと、同じ秒でした。 microtime(true) で 2 つのリクエストをログに記録します。 2 つのリクエスト間の時間差は実際には 0.0001 秒です。つまり、$redis->setex($key,10,1) はまだ正常に実行されておらず、2 番目のリクエストは正常に実行されています。すでに最初のリクエストと同じ結果が得られています。これは伝説の同時リソースの先取りではないでしょうか?このような状況は何度も聞いていましたが、開発過程で意図的にシミュレーションしたわけではありません。
3. 解決策
オプション 1: 最初の反応は、処理プロセスにトランザクションを追加することです (データベースは mysql innoDB)。トランザクションを追加した結果、最初のリクエストは成功し、2 番目のリクエストは実行されます。後で重複が見つかるとロールバックされます。実際、mysql トランザクションはデータの一貫性を確保するのに非常に優れていますが、ロールバックを通じて固有のリソースを確実に排他的に使用するにはコストがかかりすぎます。mysql トランザクションのテストを行った学生は、トランザクション内の挿入がすでに挿入されていることを知っており、ロールバックします。後で削除されました。
オプション 2: もう 1 つのオプションは、php のファイル排他ロックです。つまり、この場合、要求された各リソースの排他的使用を実現するには、ユーザー数 * タスク数で新しいファイルを作成する必要があります。排他的リソースが少ない場合はオプションです。 解決策:
$key = "a_b::".$uid.'_'.$taskid; $v = $redis->get($key); if($v == 1){ $redis->setex($key,10,1); //处理逻辑省略 }
オプション 3: $redis->setnx() がアトミック操作のステータスを提供できることがわかりました: setnx の実行後に同じキーが期限切れになっていない、または削除されていない。それを実行すると false が返されます。これにより、3 つ以上の同時リクエストを制御できるようになり、続行するにはロックを正常に取得する必要があります。
/** * 加锁 */ public function file_lock($filename){ $fp_key = sha1($filename); $this->fps[$fp_key] = fopen($filename, 'w+'); if($this->fps[$fp_key]){ return flock($this->fps[$fp_key], LOCK_EX|LOCK_NB); } return false; } /** * 解锁 */ public function file_unlock($filename){ $fp_key = sha1($filename); if($this->fps[$fp_key] ){ flock($this->fps[$fp_key] , LOCK_UN); fclose($this->fps[$fp_key] ); } }
setNX と Expire の 2 つの操作が実際に Redis トランザクションを使用して一貫性を確保できることを説明します