redis使用できるロックコマンドは、INCR、SETNX、SETだ。
最初のロックコマンドINCR
このアイデアは、キーが存在しない場合、キーの値は0に初期化され、その後INCRオペレーションが実行されて1が追加されるというものです。
その後、他のユーザーがINCR操作を行って1つ追加し、返された数値が1より大きければ、ロックが使用されていることを意味します。
1クライアントAは、ロックが取得されたことを示す値1の鍵の取得をサーバーに要求する。
2クライアントBはまた、値2のキーを取得するようサーバーに要求しており、これはロックの取得に失敗したことを意味する。
3クライアントAがコードを実行し、ロックを解除する。
4クライアントBは、ロックを要求する前に一定時間待機し、キーの値は1であり、ロックが正常に取得されたことを示す。
5クライアントBがコードを実行し、ロックを解除する。
$redis->incr($key);
$redis->expire($key, $ttl); //生成時間を1秒に設定する
第二ロックSETNX
このロックの背後にある考え方は、キーが存在しない場合、キーを値に設定します。
キーが既に存在する場合、SETNXは何もしません。
1クライアントAはサーバーにキーの値を設定するよう要求し、設定が成功すればロックが成功したことになる。
2クライアントBもサーバーにキーの値を設定するようリクエストし、失敗を返したら、ロックが失敗したことを意味する。
3クライアントAがコードを実行し、ロックを解除する。
4クライアントBは、キーの値を要求する前にしばらく待ち、設定が成功する。
5クライアントBがコードを実行し、ロックを解除する。
$redis->setNX($key, $value);
$redis->expire($key, $ttl);
第三のロックSET
上記の2つの方法には1つの問題があります。では、なぜキーの有効期限を設定する必要があるのでしょうか?何らかの理由でリクエストの実行が予期せず終了し、ロックが作成されたにもかかわらず削除されなかった場合、ロックは常にそこに存在し、キャッシュは二度と更新されません。ですから、万が一に備えてロックに有効期限を設定する必要があります。
しかし、Expireで設定してもアトミックではありません。そのため、トランザクションを使用してアトミック性を確保することもできますが、これにはまだいくつかの問題があります。そこで、バージョン2.6.12以降、有効期限を設定する機能を備えたSETコマンドを使用することが公式に言及されています。
1クライアントAはサーバーにキーの値を設定するよう要求し、設定が成功すればロックが成功したことになる。
2クライアントBもサーバーにキーの値を設定するようリクエストし、失敗を返したら、ロックが失敗したことを意味する。
3クライアントAがコードを実行し、ロックを解除する。
4クライアントBは、キーの値を要求する前にしばらく待ち、設定が成功する。
5クライアントBがコードを実行し、ロックを解除する。
$redis->set($key, $value, array('nx', 'ex' => $ttl)); //ex
その他の質問
上記のステップは、ニーズを満たしているが、まだ他の問題を考慮する必要がありますか?
1、redisは、ロックが失敗したことがわかりましたか?割り込み要求または循環要求?
2、循環要求、ロックの取得の1つは、ロックの取得の他の場合は、ロックの強盗が発生する可能性があります傾向がないですか?
3、ロックが早く期限切れになった後、クライアントAが実行を終了していない、その後、クライアントBがロックを取得し、その後、クライアントAが実行を終了すると、ロックを削除するときにBのロックを削除しますか?
ソリューション
問題1:循環リクエストを使ってロックを取得します。
問題2:2つ目の問題では、ロックを取得するループリクエストにsleep関数を追加し、数ミリ秒待ってからループを実行します。
問題3:ロックを追加する場合、キーはランダムに保存されます。この場合、キーを削除するたびに、キーの中の値が保存した値と同じかどうかを判断します。
do { //問題1では、ループを使う
$timeout = 10;
$roomid = 10001;
$key = 'room_lock';
$value = 'room_'.$roomid; //質問3に対してランダムな値を割り当てる
$isLock = Redis::set($key, $value, 'ex', $timeout, 'nx');//ex
if ($isLock) {
if (Redis::get($key) == $value) { //他のリクエストによって作成されたロックの早期期限切れや誤削除を防ぐ
//内部コードを実行する
Redis::del($key);
continue;//実行は正常にキーを削除し、ループから飛び出す
}
} else {
usleep(5000); //スリープ、ロックグラブの頻度を減らす、redisの圧力を緩和する、問題2について
}
} while(!$isLock);
高度な多くのPHPerは、常にいくつかの問題やボトルネックに遭遇し、ビジネスコードは、方向感覚よりも多くを記述するために、私はいくつかの情報を照合している改善するためにどこから始めればわからないが、これらに限定されない:分散アーキテクチャ、高いスケーラビリティ、高性能、高同時性、サーバーのパフォーマンスチューニング、TP6、laravel、YII2、Redis、Swoole、Swoft、Kafka、Mysqlの最適化、シェルスクリプト、Docker、マイクロサービス、Nginx、その他多くの高度な高度な乾物は、みんなと自由に共有する必要があります!