ホームページ  >  記事  >  データベース  >  Redis で分散ロックが必要なのはなぜですか?どのように達成するか?

Redis で分散ロックが必要なのはなぜですか?どのように達成するか?

青灯夜游
青灯夜游転載
2021-10-20 10:37:584294ブラウズ

この記事では、Redis の分散ロック、分散ロックが必要な理由、Redis が分散ロックを実装する方法について紹介します。

Redis で分散ロックが必要なのはなぜですか?どのように達成するか?

#分散ロックが必要な理由

分散ロックが必要な理由

使用分散ロックの目的は、共有リソース上で同時に 1 つのクライアントだけが操作できるようにすることだけにすぎません。

分散アプリケーションで論理処理を実行するときに、同時実行性の問題がよく発生します。 [関連する推奨事項:

Redis ビデオ チュートリアル ]

たとえば、操作にはユーザーのステータスを変更する必要があります。ステータスを変更するには、まずユーザーのステータスを読み取り、メモリ内で変更する必要があります。 、変更後に保存し直します。このような操作を同時に実行すると、状態の読み取りと保存という 2 つの操作がアトミックではないため、同時実行性の問題が発生します。

現時点では、プログラムの同時実行を制限するために分散ロックが使用されます。キャッシュ ミドルウェア システムとして、redis はこの種の分散ロック メカニズムを提供できます。

本質は、redis のピットを占有することです。他のプロセスがそのピットを占有しようとするとき、彼らはすでにピットを占有していることに気づきます。ピットが占有されています。占有されている場合は、しばらく待って、後でもう一度試してください

# 一般的に、運用環境で使用できる分散ロックは次の点を満たす必要があります:

相互排除、相互排他はロックの基本機能です。同時にロックを保持して重要な操作を実行できるのは 1 つのスレッドだけです。
  • タイムアウト解除、タイムアウト解除も必要です。ロックの機能を使用すると、MySQL InnoDB エンジンの
  • innodb_lock_wait_timeout
  • 設定を比較して、タイムアウト解除による不必要なスレッドの待機とリソースの浪費を防ぐことができます。分散環境での 再入性も同じです。ノード 同じスレッドがロックを取得した場合でも、リクエストは再び成功する可能性があります;
実装方法

SETNX を使用して実装

SETNX の使用方法は:

SETNX key value

. キー key が存在しない場合のみ、key key の値が value に設定されます. key key が存在する場合、SETNX何もアクションを起こしません。 <pre class="brush:js;toolbar:false;">boolean result = jedis.setnx(&quot;lock-key&quot;,true)== 1L; if (result) { try { // do something } finally { jedis.del(&quot;lock-key&quot;); } }</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 秒にリセットされます

RedLock

クラスターでは、マスター ノードがハングアップすると、スレーブ ノードが代わりに動作しますが、クライアントには明確な認識はありません。最初のクライアントはマスター ノードのロックの申請に成功しましたが、ロックがスレーブ ノードに同期される前に、マスター ノードが突然停止したことが判明しました。その後、スレーブ ノードがマスター ノードになり、この新しいノードは内部にこのロックを持たないため、別のクライアントがロックを要求してきた場合、即座に承認されます。これにより、システム内の同じロックが 2 つのクライアントによって同時に保持されることになり、セキュリティが低下します。

Redlock アルゴリズムは、この問題を解決するためのものです。

Redlock を使用するには、複数の Redis インスタンスを提供する必要があります。これらのインスタンスは以前は互いに独立しており、マスター/スレーブ関係がありませんでした。多くの分散アルゴリズムと同様に、レッドロックもほとんどのメカニズムを使用します

ロックするとき、半分以上のノードにセット命令を送信します。半分以上のノードsetが成功する限り、ロックは成功したとみなされます。ロックを解除する場合は、全ノードにdel命令を送信する必要があります。ただし、Redlock アルゴリズムでは、エラー リトライやクロック ドリフトなどの多くの詳細な問題も考慮する必要があり、同時に Redlock は複数のノードに対して読み書きする必要があるため、Redis のパフォーマンスが低下することを意味します。

Redlock アルゴリズムは、単一の Redis ノードに基づいて導入された高可用性モードです。Redlock は、N 個の完全に独立した Redis ノードに基づいており、通常は 3 より大きい奇数です。 (通常、N は 5 に設定できます)。これにより、基本的にクラスターの各ノードが同時にダウンしないことが保証されます。

現在のクラスターに 5 つのノードがあると仮定すると、Redlock アルゴリズムを実行しているクライアントは、ロックの取得操作を完了するために次の手順を実行します。

  • クライアント レコード 現在のシステム時間 (ミリ秒単位);
  • 同じキーを順番に使用して 5 つの Redis インスタンスからロックを取得しようとします。Redis からのロックの取得を要求する場合、クライアントはネットワーク接続と応答タイムアウトを設定する必要があります。 、ネットワーク障害による問題を避けるために、タイムアウトはロックの有効期限よりも短くする必要があります;
  • クライアントは、現在の時刻からロックの取得を開始する時刻を引いた時刻を使用して、ロックの取得時間を取得します。半分からの場合に限り、上記の Redis ノードがロックを取得し、使用時間がロックの有効期限よりも短い場合、ロックは正常に取得されます。
  • ロックが取得された場合、実際の有効時間はキーは、有効時間からロックの取得に使用された時間を引いたものに等しいです。タイムアウトの可能性を減らすための時間です。
  • ロックの取得が失敗した場合、クライアントはノードを含むすべての Redis インスタンスでロックを解除する必要があります。前の操作リクエストが失敗した場合、サーバー応答メッセージが失われるのを防ぐためですが、実際のデータの追加が成功したために不整合が発生しました。

つまり、ロックの期限が 30 秒で切れ、3 つのノードをロックするのに 31 秒かかると仮定すると、当然ロックは失敗します。

Redis が公式に推奨する Java クライアントではRedisson には RedLock

https://redis.io/topics/distlock

https:/ の組み込み実装があります/ github.com/redisson/redisson/wiki

RedLock の問題:

RedLock はロックの高可用性を保証するだけで、正確性は保証しませんロックの

RedLock はシステム クロックに大きく依存する 分散システムです

Martin の RedLock に対する批判:

  • 効率向上のためシナリオでは、RedLock が重すぎます。
  • 非常に高い精度が必要なシナリオの場合、RedLock は精度を保証できません。

この記事は https://juejin.cn/post/7018968452136173576

著者: 遠い心で

から転載しましたプログラミング関連の詳細については、プログラミング ビデオ をご覧ください。 !

以上がRedis で分散ロックが必要なのはなぜですか?どのように達成するか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。