この記事では、Redis の分散ロック、分散ロックが必要な理由、Redis が分散ロックを実装する方法について紹介します。
分散ロックが必要な理由
使用分散ロックの目的は、共有リソース上で同時に 1 つのクライアントだけが操作できるようにすることだけにすぎません。 分散アプリケーションで論理処理を実行するときに、同時実行性の問題がよく発生します。 [関連する推奨事項:Redis ビデオ チュートリアル ]
たとえば、操作にはユーザーのステータスを変更する必要があります。ステータスを変更するには、まずユーザーのステータスを読み取り、メモリ内で変更する必要があります。 、変更後に保存し直します。このような操作を同時に実行すると、状態の読み取りと保存という 2 つの操作がアトミックではないため、同時実行性の問題が発生します。 現時点では、プログラムの同時実行を制限するために分散ロックが使用されます。キャッシュ ミドルウェア システムとして、redis はこの種の分散ロック メカニズムを提供できます。本質は、redis のピットを占有することです。他のプロセスがそのピットを占有しようとするとき、彼らはすでにピットを占有していることに気づきます。ピットが占有されています。占有されている場合は、しばらく待って、後でもう一度試してください
# 一般的に、運用環境で使用できる分散ロックは次の点を満たす必要があります:相互排除、相互排他はロックの基本機能です。同時にロックを保持して重要な操作を実行できるのは 1 つのスレッドだけです。
再入性も同じです。ノード 同じスレッドがロックを取得した場合でも、リクエストは再び成功する可能性があります; SETNX の使用方法は:
SETNX key value. キー key が存在しない場合のみ、key key の値が value に設定されます. key key が存在する場合、SETNX何もアクションを起こしません。 <pre class="brush:js;toolbar:false;">boolean result = jedis.setnx("lock-key",true)== 1L;
if (result) {
try {
// do something
} finally {
jedis.del("lock-key");
}
}</pre>
この解決策には致命的な問題があります。つまり、スレッドがロックを取得し、何らかの異常な要因 (ダウンタイムなど) によりロック解除操作を正常に実行できなくなると、ロックは決して解放されなくなります。
この目的のために、このロックにタイムアウト期間を追加できます
SET キー値 EX 秒の実行の効果は、SETEX キー秒の実行と同等です。 value
##SET キー値 PX ミリ秒
PSET キー ミリ秒値
String result = jedis.set("lock-key",true, 5); if ("OK".equals(result)) { try { // do something } finally { jedis.del("lock-key"); } }
の実行と同等です。ソリューションは完璧に見えますが、実際にはまだ問題があります。
特定のスレッド A がロックを取得し、有効期限を 10 秒に設定し、ビジネス ロジックの実行に 15 秒かかると想像してください。このとき、スレッド A が取得します。 ロックは Redis の有効期限機構によりすでに自動的に解放されています。
スレッド A がロックを取得し、10 秒が経過すると、変更されたロックが他のスレッドによって取得されている可能性があります。スレッド A がビジネス ロジックの実行を終了し、ロック解除 (DEL キー
) の準備をすると、他のスレッドによって取得されたロックを削除できます。したがって、最良の方法は、ロックを解除するときに、ロックが自分のものであるかどうかを判断することです。
key
uniqueValue を設定するときに、値を一意の値に設定できます (ランダムな値、UUID、またはマシン番号とスレッド番号の組み合わせ、署名など)。
ロックを解除するとき、つまりキーを削除するときは、まずそのキーに対応する値が以前に設定した値と等しいかどうかを判定し、等しい場合にはキーを削除できます
String velue= String.valueOf(System.currentTimeMillis()) String result = jedis.set("lock-key",velue, 5); if ("OK".equals(result)) { try { // do something } finally { //非原子操作 if(jedis.get("lock-key")==value){ jedis.del("lock-key"); } } }問題はここで一目でわかります さあ:
GET
とDEL は 2 つの別個の操作です。GET 実行と DEL 実行の間のギャップで例外が発生する可能性があります。
ロック解除コードがアトミックであることを確認するだけでよい場合は、問題を解決できます。
Lua スクリプト
を紹介します。例は次のとおりです。if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 endここで、ARGV[1] は、キーの設定時に指定された一意の値を表します。
Lua スクリプトの原子性により、Redis がスクリプトを実行するプロセス中、他のクライアント コマンドは、Lua スクリプトが実行されるまで待機してから実行する必要があります。
有効期限がビジネス実行時間よりも長いことを確認してください
複数のスレッドがビジネス コードを同時に実行しないようにするには、次のことを確認する必要があります。有効期限がビジネス実行時間より大きいことを確認します。
ブール型属性isOpenExpirationRenewal
を追加します。これは、スケジュールされた更新有効期限を有効にするかどうかを識別するために使用されます。さらに、
scheduleExpirationRenewal
ロック コードは、ロックの取得に成功した後、isOpenExpirationRenewal を true に設定し、
scheduleExpirationRenewal
ロック解除コードはコード行を追加し、isOpenExpirationRenewal 属性を設定します。false の場合、有効期限を更新するスレッドのポーリングを停止します
Redisson の実装
ロックが正常に取得されると、スケジュールされたタスクが開始され、スケジュールされたタスクは更新のために定期的にチェックされます
このスケジュールされたスケジュールの各呼び出し間の時間差は internalLockLeaseTime / 3
で、これは 10 秒です
デフォルトでは、ロック時間は 30 秒です。実行後、30-10 = 20
秒になると更新が行われ、ロックが 30 秒にリセットされます
クラスターでは、マスター ノードがハングアップすると、スレーブ ノードが代わりに動作しますが、クライアントには明確な認識はありません。最初のクライアントはマスター ノードのロックの申請に成功しましたが、ロックがスレーブ ノードに同期される前に、マスター ノードが突然停止したことが判明しました。その後、スレーブ ノードがマスター ノードになり、この新しいノードは内部にこのロックを持たないため、別のクライアントがロックを要求してきた場合、即座に承認されます。これにより、システム内の同じロックが 2 つのクライアントによって同時に保持されることになり、セキュリティが低下します。
Redlock アルゴリズムは、この問題を解決するためのものです。
Redlock を使用するには、複数の Redis
インスタンスを提供する必要があります。これらのインスタンスは以前は互いに独立しており、マスター/スレーブ関係がありませんでした。多くの分散アルゴリズムと同様に、レッドロックもほとんどのメカニズムを使用します
ロックするとき、半分以上のノードにセット命令を送信します。半分以上のノードset
が成功する限り、ロックは成功したとみなされます。ロックを解除する場合は、全ノードにdel命令を送信する必要があります。ただし、Redlock アルゴリズムでは、エラー リトライやクロック ドリフトなどの多くの詳細な問題も考慮する必要があり、同時に Redlock
は複数のノードに対して読み書きする必要があるため、Redis のパフォーマンスが低下することを意味します。
Redlock アルゴリズムは、単一の Redis ノードに基づいて導入された高可用性モードです。Redlock は、N 個の完全に独立した Redis ノードに基づいており、通常は 3 より大きい奇数です。 (通常、N は 5 に設定できます)。これにより、基本的にクラスターの各ノードが同時にダウンしないことが保証されます。
現在のクラスターに 5 つのノードがあると仮定すると、Redlock アルゴリズムを実行しているクライアントは、ロックの取得操作を完了するために次の手順を実行します。
つまり、ロックの期限が 30 秒で切れ、3 つのノードをロックするのに 31 秒かかると仮定すると、当然ロックは失敗します。
Redis が公式に推奨する Java クライアントではRedisson
には RedLock
https://redis.io/topics/distlock
https:/ の組み込み実装があります/ github.com/redisson/redisson/wiki
RedLock の問題:
RedLock はロックの高可用性を保証するだけで、正確性は保証しませんロックの
RedLock はシステム クロックに大きく依存する 分散システムです
Martin の RedLock に対する批判:
この記事は https://juejin.cn/post/7018968452136173576
著者: 遠い心で
から転載しましたプログラミング関連の詳細については、プログラミング ビデオ をご覧ください。 !
以上がRedis で分散ロックが必要なのはなぜですか?どのように達成するか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。