誰もが推奨する Redis 分散ロックは本当に万全なのでしょうか?

誰もが推奨する Redis 分散ロックは本当に万全なのでしょうか?

単一インスタンス JVM には、アクセス制御用の synchronized キーワード、volatile キーワード、ReentrantLock などの一般的なメソッドなど、同時実行性の問題に対処するための一般的なメソッドが多数あります。ただし、分散環境では、上記の方法を使用して、JVM 間のシナリオにおける同時実行の問題を処理することはできません。ビジネス シナリオで分散環境での同時実行の問題を処理する必要がある場合は、分散ロックを使用して実装する必要があります。

[[268881]]

分散ロックとは、分散展開環境でロック メカニズムを使用して、複数のクライアントが共有リソースに相互に排他的にアクセスできるようにすることです。

現在、より一般的な分散ロック実装ソリューションは次のとおりです。

  • MySQLなどのデータベースに基づく
  • Redisなどのキャッシュに基づく
  • Zookeeper、etcd などに基づいています。

ここでは、キャッシュ(Re​​dis)を使用して分散ロックを実装する方法を紹介します。

Redis を使用して分散ロックを実装する最も簡単なソリューションは、SETNX コマンドを使用することです。 SETNX (SET if Not eXist) の使用方法は次のとおりです: SETNX キー値。キー key が存在しない場合にのみ、キー key の値が value に設定されます。キー key が存在する場合、SETNX は何もアクションを実行しません。 SETNX は設定が成功した場合は を返し、失敗した場合は 0 を返します。ロックを取得したい場合は、SETNX を使用してロックを取得するだけです。ロックを解除したい場合は、DEL コマンドを使用して対応するキーを削除します。

上記の解決策には致命的な問題があります。つまり、スレッドがロックを取得した後、何らかの異常な要因 (クラッシュなど) によりロック解除操作を正常に実行できず、ロックが解放されなくなります。これを実行するには、このロックにタイムアウトを追加します。 ***時間は、Redis の EXPIRE コマンド (EXPIRE キーの秒数) を思い出させます。ただし、EXPIRE と SETNX は 2 つの操作であり、2 つの操作の間に例外が発生する可能性があるため、ここでは分散ロックを実装するために EXPIRE を使用することはできません。そのため、期待される結果は得られません。例は次のとおりです。

  1. // ステップ 1
  2. SETNXキー
  3. // ここで(STEP1とSTEP2の間)プログラムが突然クラッシュすると、有効期限を設定できず、ロックが解除されない可能性があります。
  4. // ステップ 2
  5. EXPIREキーの有効期限

これに対する正しいアプローチは、「SET キー値 [EX 秒] [PX ミリ秒] [NX|XX]」コマンドを使用することです。

Redis バージョン 2.6.12 以降では、SET コマンドの動作は一連のパラメータによって変更できます。

  • EX seconds: キーの有効期限を seconds 秒に設定します。 SET key value EX seconds を実行した場合の効果は、SETEX key seconds value を実行した場合と同じです。
  • PX ミリ秒: キーの有効期限をミリ秒に設定します。 SET キー値 PX ミリ秒を実行した場合の効果は、PSETEX キー ミリ秒値を実行した場合の効果と同じです。
  • NX: キーが存在しない場合にのみ設定します。 SET キー値 NX を実行した場合の効果は、 SETNX キー値 を実行した場合と同じです。
  • XX: キーがすでに存在する場合にのみ設定します。

たとえば、分散ロックを作成し、有効期限を 10 秒に設定するには、次のコマンドを実行します。

  1. SETロックキー ロック値 EX 10 NX
  2. または
  3. SET lockKey lockValue PX 10000 NX

EX と PX を同時に使用することはできません。同時に使用すると、ERR 構文エラーというエラーが報告されます。

ロックを解除するときは、引き続き DEL コマンドを使用してロックを解除します。

修正された計画は素晴らしいように見えますが、まだ問題があります。スレッド A がロックを取得し、有効期限を 10 秒に設定し、ビジネス ロジックの実行に 15 秒かかるとします。この時点で、スレッド A によって取得されたロックは、Redis の有効期限メカニズムによってすでに自動的に解放されています。スレッド A がロックを取得してから 10 秒が経過すると、他のスレッドによってロックが取得されている可能性があります。スレッド A がビジネス ロジックの実行を終了し、ロック解除 (DEL キー) の準備を行うと、他のスレッドが取得したロックが削除される可能性があります。

したがって、最善の方法は、ロックを解除するときに、ロックが自分のものであるかどうかを確認することです。キーを設定するときに、値を一意の値 uniqueValue に設定できます (ランダムな値、UUID、またはマシン番号 + スレッド番号の組み合わせ、署名など)。ロックを解除する、つまりキーを削除するときは、まずキーに対応する値が以前に設定された値と等しいかどうかを判断します。等しい場合は、キーを削除できます。疑似コードの例は次のとおりです。

  1. uniqueKey == GET(キー)の場合{
  2. DELキー 
  3. }

ここで問題となるのは、GET と DEL が別々の操作であり、GET の実行と DEL の実行の間のギャップで例外が発生する可能性があることです。ロック解除コードがアトミックであることを保証する必要があるだけであれば、問題は解決できます。ここでは、Lua スクリプトという新しい方法を紹介します。例は次のとおりです。

  1. redis.call( "get" ,KEYS[1]) == ARGV[1]の場合 
  2. redis.call( "del" ,KEYS[1])を返す
  3. それ以外 
  4. 0を返す
  5. 終わり 

ARGV[1]はキー設定時に指定された一意の値を表します。

Lua スクリプトのアトミック性のため、Redis がスクリプトを実行している場合、他のクライアントからのコマンドは Lua スクリプトが完了するまで待機してから実行する必要があります。

次に、Jedis を使用して、次のようにロックの取得とロック解除の実装を示します。

  1. パブリックブールロック(文字列ロックキー、文字列一意の値、 int秒){
  2. SetParams パラメータ = 新しい SetParams();
  3. params.nx().ex(秒);
  4. 文字列結果 = jedis.set (lockKey、uniqueValue、params);
  5. if ( "OK" .equals(結果)) {
  6. 戻る 真実;
  7. }
  8. 戻る 間違い;
  9. }
  10. パブリックブールロック解除(文字列ロックキー、文字列一意の値){
  11. 文字列スクリプト = "if redis.call('get', KEYS[1]) == ARGV[1] " +
  12. 「その後、redis.call('del', KEYS[1]) を返し、そうでない場合は 0 を返して終了します」 ;
  13. オブジェクト結果 = jedis.eval(script,
  14. コレクション.シングルトンリスト(ロックキー)、
  15. Collections.singletonList(uniqueValue));
  16. (結果が1に等しい場合){
  17. 戻る 真実;
  18. }
  19. 戻る 間違い;
  20. }

これは絶対確実ですか?明らかに違います!

表面的には、この方法は機能しているように見えますが、ここには問題があります。システム アーキテクチャに単一障害点があるのです。 Redis マスターノードがダウンしたらどうなりますか? 「スレーブノードを追加してください」と言う人もいるかもしれません。マスターがダウンしたらスレーブを使用してください。

しかし実際には、Redis レプリケーションは非同期であるため、このソリューションは明らかに実現可能ではありません。例えば:

  1. スレッド A はマスター ノードのロックを取得します。
  2. A によって作成されたキーをスレーブに書き込む前に、マスター ノードがクラッシュしました。
  3. スレーブノードがマスターノードになります。
  4. スレッド B も、スレッド A が保持しているのと同じロックを取得します。 (元のスレーブはロックを保持しているAに関する情報を持っていないためです)

もちろん、この解決策はいくつかのシナリオでは適切です。たとえば、ビジネス モデルで同時ロック保持が許可されている場合は、このソリューションを使用できます。

たとえば、サービスには A と B という 2 つのサービス インスタンスがあります。最初に、A はロックを取得してリソースを操作します (この操作はリソースを大量に消費すると想定できます)。一方、B はロックを取得せず、操作も実行しません。このとき、B は A のホットスタンバイとみなすことができます。A が異常な動作をした場合、B を「正常化」することができます。 Redis マスターがクラッシュするなど、ロックが異常な場合、B はロックを保持し、同時にリソースを操作する可能性があります。操作の結果がべき等である場合(またはその他の状況)は、このソリューションも使用できます。ここで分散ロックを導入すると、通常の状況下での繰り返し計算やリソースの浪費をサービスが回避できるようになります。

この状況に対処するために、Antriez は Redlock アルゴリズムを提案しました。 Redlock アルゴリズムの主なアイデアは、完全に独立した N 個の Redis マスター ノードがあると仮定することです。前のソリューションを使用して、ロックを取得し、前の単一の Redis マスター ノードのロックを解除できます。妥当な範囲内でロックを取得できる場合、または全体で N/2+1 個のロックを取得できる場合は、ロックが正常に取得されたと見なすことができます。それ以外の場合は、ロックは取得されていません (クォーラム モデルと比較できます)。 Redlock の原理は理解しやすいですが、内部実装の詳細は非常に複雑であり、多くの要素を考慮する必要があります。

Redlock アルゴリズムは「万能薬」ではありません。やや厳しい条件に加え、アルゴリズム自体も疑問視されている。 Redis 分散ロックのセキュリティに関しては、分散システムの専門家 Martin Kleppmann 氏と Redis の作者 antirez 氏の間で議論がありました。

<<:  選ばれたソリューションが生まれ、その背後にある3つの価値が説明される

>>:  クラウドコンピューティング技術が大企業に与える影響

推薦する

sharktech-$89/i3-2100/8g メモリ/1.5t ハードディスク/無制限 G ポート/29IP/IPMI/DDoS 保護

Sharktech (DDoS サーバーの有名な米国ブランド) は今月、特別価格のサーバーをいくつか...

QingCloud Technologyがクラウドネイティブを包括的に展開、KubeSphereがさらなるクラウドネイティブパワーをリリース

[51CTO.com からのオリジナル記事] クラウド ネイティブは、現在のあらゆるテクノロジー カ...

Qvodは付加価値通信事業ライセンスを取り消され、ウェブサイトも閉鎖された。

[要約] 商業ウェブサイトがインターネットにアクセスし、広告を運用するには、付加価値通信事業ライセン...

クラウドコンピューティングにはどのような経済的価値がありますか?

クラウドコンピューティングは、その誕生以来、社会を変える「汎用技術」として位置づけられてきました。し...

Kafka を浅くから深く学ぶ: プロデューサー メッセージ パーティション メカニズムの原理

[[322641]] Apache Kafka を使用してメッセージを生成および消費する場合、データ...

ソーシャルメディアトレンドレポート!

このレポートでは、ソーシャル メディアのインタラクティブな参加、ソーシャル メディアの見通し、ソーシ...

クラウド移行の課題と利点は何ですか?

クラウドコンピューティング技術企業の IT リソースをクラウドに移行する理由は、3 つの柱に基づいて...

WeChatの運営に関するあまり知られていない興味深い事実、WeChatの宣伝と運営方法

上記のクールで少しよそよそしいフォーマットはどうやって作成したのですか?公式アカウントの品質向上に役...

中国企業のデジタルトランスフォーメーションを全面的にサポートするVMware vFORUM 2018を開催

[51CTO.comよりオリジナル記事] 11月16日、VMwareの年次イベントvFORUM 20...

医療ウェブサイトは今後もニュースソースであり続けるべきでしょうか?

ウェブマスターの友人たちは、ニュース ソースが何であるかをすでに知っていると思います。簡単に言えば、...

ニュース: V.PS、アムステルダムとフランクフルトをAS4809+AS9929に無料でアップグレード

朗報です。v.ps はまず、オランダのアムステルダムとドイツのフランクフルトのデータセンターの VP...

Baidu マーケティングにはリスクがつきものです。ウェブマスターが知っておくべき 3 つのよくある誤解

大多数のウェブマスターにとって、Baidu は愛憎関係です。多くのウェブマスターが Baidu のお...

クラウドネイティブアプリケーションを保護する方法

[[427938]] [51CTO.com クイック翻訳]現在、多くの企業がクラウドネイティブ設計パ...

クラウドへの効果的な移行のための 7 つのヒント

[[358359]]調査会社ガートナーは、適切な戦略を採用しないと、クラウド プラットフォームに移行...