Redis 分散ロックの話題はよくあるようです。面接中や仕事中など、どこでも見ることができます。 「コードブラザー」はなぜまだそれについて書く必要があるのでしょうか? なぜなら、インターネット上の記事の 99.99% は分散ロックを実際に明確に説明しておらず、バグも多数あるからです。 今日は「コード兄さん」が皆さんと一緒に分散ロックのGスポットに深く入り込み、体系的に良いコードを書き、スキルをマスターする本当の男になります。 「クライマックス」を迎える前に、「前戯」として以下の質問について考えてみましょう。いくつ答えられますか?
分散ロックはなぜ必要なのでしょうか?マー兄さん、分散ロックがいつ必要になるかを説明する簡単な例を挙げてもらえますか? 精子が射精される瞬間、何十億もの精子が卵子に向かって突進し、幸運にも卵子と結合できる精子はたった 1 つだけです。 たった 1 つの「精子」だけが「卵子」に好まれるようにするために、創造主は、1 つの精子が卵子に入ると卵子の外殻を変え、経路を閉じて残りの精子をブロックします。 数十億の精子は「同時」交通のようなものです。 卵は共有資源のようなものです。 卵子の殻にある特殊なタンパク質が鍵のような働きをして、精子が 1 個だけ入るのを許可します。 複数のノードで構成されるクラスターには、複数の JVM プロセスが存在します。同じ効果を得るには、調整を行う仲介者が必要であり、JVM 内の 1 つのスレッドのみが共有リソースを操作できるようにする必要があります。 分散ロックは、JVM プロセス内の 1 つのスレッド「精子」のみが同時に保護されたリソース「卵子」にアクセスできるように制御するために使用されます。 「すべての命は何十億ものプレイヤーの中のリーダーだ」、そうか。 分散ロックの概要分散ロックはどのような特性を満たす必要がありますか?
コード兄弟、SETNX キー値コマンドを使用して「相互排他」機能を実装できます。 このコマンドは、SET if Not eXists の略語に由来しており、キーが存在しない場合はこのキーに値を設定し、それ以外の場合は何もしないことを意味します。 コマンドは次を返します:
次のシナリオ: 一日中コードを入力して疲れているので、リラックスして肩と首をマッサージしたいです。 技術者 168 号は最も人気があり、誰もが彼に注文を付けたがるので、同時実行性が高く、分散ロック制御が必要です。 168 人の技術者と同時に予約を取ることができるのは 1 人の「顧客」だけです。 肖彩吉は168人の技術者の募集に合格しました:
謝覇は後から到着したが、彼の申請は却下された。
現在、お申し込みに成功したお客様は、168名の技術者による「リソース共有」による肩・首のリラクゼーションサービスをご利用いただけます。 楽しみが終わったら、後から来た人が 168 人の技術者のサービスを受けられるように、ロックを時間通りに解除する必要があります。 Xiao Caijiさん、ロックを解除する方法を教えていただけますか? とても簡単です。DEL を使用してキーを削除するだけです。
マー兄さん、「ドラゴン」を見たことがありますか?はい、ドラゴンに仕えられたからです。 シャオ・ツァイジ、物事はそんなに単純じゃないよ。 このソリューションには「デッドロック」を引き起こす問題があり、この問題を引き起こすシナリオは次のとおりです。 マッサージ中に突然オンラインアラームが届いたので、ズボンを上げて会社に駆けつけましたが、ロックを解除するためにDELを間に合うように実行しませんでした(クライアントがビジネスを異常に処理し、ロックを正しく解除できませんでした)。 マッサージ中に心筋梗塞で亡くなり、DEL コマンドを実行できませんでした。 この方法では、ロックは常に使用中となり、他の顧客は技術者のサービスを受ける機会が「決して」なくなります。 デッドロックを回避する方法コード兄弟、ロックが正常に取得されたときに「タイムアウト」を設定できます たとえば、マッサージ サービスを 60 分間設定したい場合は、キーをロックするときに 60 分の有効期限を設定できます。
これにより、時間経過後にロックが自動的に解除され、他のお客様は引き続き 168 人の技術者によるマッサージ サービスを受けることができます。 誰かがこのように書いたらひどいことになるでしょう。 「ロック」と「タイムアウトの設定」は 2 つのコマンドであり、アトミック操作ではありません。 最初の項目のみが実行され、2 番目の項目が実行される機会がない場合、「タイムアウト」設定は失敗し、デッドロックが発生します。 たとえば、次のシナリオでは、2 番目の命令の実行が失敗します。 Redis が異常クラッシュしました。 クライアントが異常クラッシュしました。 ママ兄さん、どうしたらいいですか?ワンストップサービスが求められ、行き詰まりがあってはなりません。 Redis 2.6.12 以降では、キーが存在しない場合に値を設定するセマンティクス、タイムアウト期間を設定するセマンティクス、およびアトミック性を満たすセマンティクスを満たすように SET コマンドのパラメータが拡張されています。
NX: resource_name が存在しない場合にのみ SET が成功することを示し、これにより 1 つのクライアントのみがロックを取得できることが保証されます。 PX 30000: このロックの自動有効期限が 30 秒であることを示します。 実行時間がロックの有効期限を超えていますワンストップサービスを安心してご利用いただけますか? いいえ、他の人のロックを解除する別のシナリオがあります。
対処すべき重要な問題が 2 つあります。
ロックタイムアウトを正しく設定するロックタイムアウトを計算するにはどうすればいいですか? 今回はランダムに書くことはできません。これは通常、テスト環境での複数のテストと複数ラウンドのストレス テストに基づいており、たとえば、平均実行時間は 200 ミリ秒と計算されます。 すると、ロック タイムアウト期間は平均実行時間の 3 ~ 5 倍に拡大されます。 なぜズームインするのですか? ロック操作ロジックにネットワーク IO 操作、JVM FullGC などが含まれている場合、オンライン ネットワークが常にスムーズに動作するとは限らず、ネットワーク ジッタのためにバッファ時間を残す必要があるためです。 1時間など、もっと大きな値に設定したほうが安全ではないでしょうか? これにこだわらないでください。どれくらい大きいのが「大きい」のでしょうか? 設定時間が長すぎると、ダウンタイムの再起動が発生すると、分散ロック サービスのすべてのノードが 1 時間以内に使用できなくなります。 オペレーターが手動でロックを削除しますか? 運用とメンテナンスに本当に負担がかからない限りは。 完璧な解決策はあるのでしょうか?どのように時間を設定しても、適切ではないようです。 ロックを取得したスレッドにデーモン スレッドを開始させて、期限切れになりそうなロックを「延長」させることができます。 ロック時に有効期限が設定され、クライアントは「デーモン スレッド」を開始して、ロックの有効期限を定期的に検出します。 ロックの有効期限が近づいているがビジネス ロジックが実行されていない場合は、ロックが自動的に更新され、有効期限がリセットされます。 意味は通じますが、書けません。 慌てる必要はありません。これらすべてのタスクをカプセル化するライブラリがすでに存在します。それはレディソンと呼ばれています。 Redisson は、Java で実装された Redis SDK クライアントです。分散ロックを使用する場合、ロックの有効期限切れを回避するために「自動更新」ソリューションを採用します。このデーモン スレッドは一般に「ウォッチドッグ」スレッドと呼ばれます。 スペースが限られているため、「Code Byte」をフォローして、Redisson の使用と原理の次の分析を聞くことができます。 他の人のロックを解除しないでください他の人のロックを解除する鍵は、DEL コマンドを「無意識に実行」することなので、ロックが自分自身によって追加されたかどうかを確認する方法を見つける必要があります。 ベルを結んだ人はそれを解かなければならない コード兄弟、ロックするときに、ロックするクライアントを表す値として「一意の識別子」を設定します。 ロックを解除するとき、クライアントは自身の「一意の識別子」とロックの「識別子」を比較して、それらが等しいかどうかを確認します。一致した場合は削除されます。それ以外の場合は、ロックを解除する権利はありません。 疑似コードは次のとおりです。
これは GET + DEL 命令の組み合わせであり、アトミック性の問題が関係していると考えたことがありますか。 次の状況を再現します。
私たちは完璧を追求する人間なので、ここでは Lua スクリプトを使用して実装し、判断と削除のプロセスがアトミック操作になるようにしています。
最後まで最適化すると、ソリューションはより「厳密」になり、対応するモデルは次のように抽象化されます。 SET lock_resource_name $unique_id NX PX $expire_time を通じて、期限切れが近づいているが実行されていないクライアントのロックを延長するデーモン スレッドが開始されます。 クライアントはビジネス ロジックを実行して共有リソースを操作します。 Lua スクリプトを通じてロックを解除します。まず、ロックが自分で追加されたかどうかを確認し、その後 DEL を実行します。 ロック解除コードの場所は重要です前回の分析に基づくと、すでに「比較的厳密な」分散ロックが存在します。 そこで、「Xie Ba Ge」はプロジェクトに分散ロックを適用するために次のコードを作成しました。疑似コードのロジックは次のとおりです。
ビジネス ロジックの実行中に例外がスローされると、プログラムはロックを解除する次のステップに進むことができなくなります。 したがって、ロックを解除するコードは finally{} ブロックに配置する必要があります。 ロック位置も問題あり。 redisLock.lock() ロック例外が発生した場合、finally{} コード ブロック命令が実行されてロックが解除されますが、この時点ではロックは正常に適用されません。 したがって、redisLock.lock(); try の外側に配置する必要があります。 まとめると、正しいコードの場所は次のとおりです。
再入可能ロックの実装 再入可能ロックを実装するにはどうすればいいですか?再入後のタイムアウトを設定するにはどうすればいいですか? スレッドがコードの一部を実行し、ロックを正常に取得して実行を続行すると、ロックされたコードに再び遭遇します。再入可能性により、スレッドは実行を継続できますが、再入可能性がない場合は、実行を継続する前にロックが解放されるのを待機し、再度ロックを正常に取得する必要があります。 再入可能性をコードで説明してください:
スレッド X がメソッド a でロックを取得した後、メソッド b の実行を継続すると仮定します。この時点で再入が不可能な場合、スレッドはロックが解放されるまで待機し、再度ロックを競合する必要があります。 ロックは明らかにスレッド X によって所有されていますが、ロックを取得する前に、スレッド X 自身がロックを解放するのを待つ必要があります。これはとても奇妙に見えます。自分を解放するよ〜 Redis ハッシュ再入可能ロックRedisson ライブラリは、Redis Hash を使用して再入可能ロックを実装します。今後は、Redisson の使い方や原理について具体的に記事を書く予定です。 スレッドがロックを取得した後、将来ロック メソッドに遭遇すると、ロックの数を直接 1 増やしてからメソッド ロジックを実行します。 ロックメソッドを終了した後、ロック回数が 1 減少します。ロック回数が 0 になると、ロックは完全に解除されます。 再入可能ロックの最大の特徴は、ロックが追加された回数を計算するカウント機能であることがわかります。 したがって、分散環境で再入可能ロックを実装する必要がある場合は、ロックの数もカウントする必要があります。 ロックロジック これを実装するには、Redis ハッシュ構造を使用できます。キーはロックされた共有リソースを表し、ハッシュ構造のフィールドキーの値にはロックの数が格納されます。 KEYS1 = "lock"、ARGV "1000, uuid" と仮定して、Lua スクリプトを通じてアトミック性を実装します。
ロック コードは、まず Redis の exists コマンドを使用して、現在のロックが存在するかどうかを判断します。 ロックが存在しない場合は、hincrby を直接使用してキー uuid を持つロック ハッシュ テーブルを作成し、ハッシュ テーブル内のキー uuid を 0 に初期化してから、再度 1 を追加し、最後に有効期限を設定します。 現在のロックが存在する場合は、hexists を使用して、キー uuid が現在のロックに対応するハッシュ テーブルに存在するかどうかを判断します。存在する場合は、hincrby を使用して再度 1 を追加し、最後に有効期限を再度設定します。 最後に、上記の 2 つのロジックが一致しない場合は、直接戻ります。 ロジックをアンロック
まず、hexists を使用して、Redis ハッシュ テーブルに特定のフィールドが含まれているかどうかを判断します。 ロックに対応するハッシュ テーブルが存在しない場合、またはキー uuid がハッシュ テーブルに存在しない場合は、直接 nil が返されます。 存在する場合、現在のロックはそれによって保持されていることを意味します。まず、hincrby を使用して再入回数を 1 減らし、計算後に再入回数を決定します。 0 以下の場合は、del を使用してロックを削除します。 ロック解除コードの実行方法はロックの場合と似ていますが、ロック解除の実行結果の戻り値の型が Long である点が異なります。ここでロックと同様にブール値が使用されていない理由は、ロック解除の Lua スクリプトでは、3 つの戻り値が次の意味を持つためです。
マスタースレーブアーキテクチャによって引き起こされる問題コード兄弟、分散ロックはここでは「完璧」ですよね?分散ロックにこれほど多くのトリックがあるとは思いませんでした。 まだ長い道のりが残っています。これまで分析したシナリオはすべて、「単一の」Redis インスタンスをロックすることで発生する可能性のある問題に関するものであり、Redis のデプロイメント アーキテクチャの詳細は関係していませんでした。 高可用性を確保するために、通常は「Cluster Cluster」または「Sentinel Cluster」モードを使用します。 どちらのモードも「マスタースレーブアーキテクチャデータ同期レプリケーション」に基づいてデータ同期を実装しており、Redis のマスタースレーブレプリケーションはデフォルトで非同期です。 次のシナリオで何が起こるか想像してみましょう。 クライアント 1 がマスター ノードに分散ロックを書き込んだばかりの場合、この命令はまだスレーブ ノードに同期されていません。 このとき、マスター ノードがクラッシュし、スレーブの 1 つが新しいマスターとして選出されます。この時点では、新しいマスターにはクライアント 1 によって書き込まれたロックがないため、ロックは失われます。 この時点で、クライアント 2 スレッドがロックの取得に成功します。 確率は極めて低いですが、このリスクの存在を認識しなければなりません。 Redisの作者はRedlockと呼ばれるソリューションを提案した。 分散ロックの標準を統一するために、Redis の作者は Redlock を作成しました。これは、分散ロックを実装するための公式の Redis ガイドと見なされています (https://redis.io/topics/distlock)。しかし、このレッドロックは海外の分散専門家からも批判されている。 なぜなら、完璧ではなく、「穴」があるからです。 レッドロックとはこれは赤い鍵ですか? インスタントラーメンを食べ過ぎると、Redlock はマスタースレーブアーキテクチャにおけるロック損失を解決するために提案されたアルゴリズムです。 Redlock のソリューションは、次の 2 つの前提に基づいています。
つまり、Redlock を使用する場合は、少なくとも 5 つの Redis インスタンスをデプロイする必要があり、それらはすべてマスター データベースである必要があります。それらの間には関連性はなく、すべて独立したインスタンスです。 クライアントがロックを取得するには、次の 5 つの手順があります。
複数のインスタンスをデプロイしてロックするのはなぜですか? 本質は高可用性とフォールト トレランスです。一部のインスタンスがダウンした場合でも、ほとんどのインスタンスは正常にロックされ、分散ロック サービス全体が引き続き利用できます。 3 番目のステップで累積ロック時間を計算する必要があるのはなぜですか? 複数のノードがロックされるため、時間がかかる場合があり、ネットワークでパケット損失やタイムアウトなどが発生する可能性があります。 ほとんどのノードがロックを正常に取得できたとしても、ロックの取得にかかる合計時間がロックの有効期間を超えると、ロックは無意味になります。 一部のノードがロックに失敗した場合でも、ロックを解除するためにすべてのノードを操作する必要があるのはなぜですか? これは、クライアントが Redis インスタンスを正常にロックしたが、応答の読み取りに失敗し、ロックが失敗したとクライアントが判断する可能性があるためです。 ロックを安全にクリアするには、ロックを解除する要求を各ノードに送信する必要があります。 Redlock はそんなに完璧なのか? Redis マスタースレーブアーキテクチャノードの異常なダウンタイムによって引き起こされるロック損失の問題を解決しますか? 物事はそんなに単純ではありません。 Redis の作者がこのソリューションを提案した後、業界の著名な分散システムの専門家から疑問が投げかけられました。 二人はまるで神々が戦うかのように、一つの問題に対して多くの結論を出すのに十分な証拠をもって、何度も議論を交わしました... スペースの制約により、次回は、Redssion の分散ロックのカプセル化と Redlock の実装に関する両者の議論の分析についてお話しします。 次に何が起こるかは、次のエピソードをお待ちください... 要約する終わったら、画面を閉じて、自分が何をしているのか、なぜそれをしているのか、どんな問題を解決しようとしているのかを考えながら、もう一度各ステップを頭の中で確認することをお勧めします。 一緒に、Redis 分散ロックのさまざまなトリックを最初から最後まで確認しました。実際、これらの点の多くは、分散ロックに何を使用しても存在する問題です。大切なのは思考のプロセスです。 システム設計に関しては、誰もが異なる出発点を持っています。完璧な建築、普遍的な建築というものは存在しませんが、完璧さと普遍性の間で良いバランスをとった建築は良い建築です。 Redlock に関する議論は主に以下の点に焦点を当てています。
|
<<: 企業がハイブリッド クラウドを採用するメリットは何でしょうか?
>>: ドイツテレコムが GitOps を使用してエッジ インフラストラクチャを管理する方法
フロリダのホスティングサービスは安いらしいと言われていますが?クリスシックなどの商品もとても安く販売...
世の中の物事は、長い統一期間を経て、やがて分離し、長い分離期間を経て、やがて統一される。巨大企業の間...
多くのウェブマスターは、Baidu の製品の重みが非常に高いことを知っています。Baidu を使用し...
網易科技ニュース、6月3日、360社は本日、独立したモバイル検索アプリ「360 Search」を正式...
分散システムでのリクエスト追跡は、Trace ID と Span ID を通じて実現され、記録された...
WordPress を使用して Web サイトを構築している友人は、タグに非常に精通しています。ラベ...
最近、私は Pulsar と Kafka の比較を調べています。ちょっと検索してみると、これら 2 ...
GlobalDots の CTO である Yair Green 氏が、人工知能と機械学習がサービスと...
最適化の不正行為はすべて不倫のようなもの今日では、インターネットについて少しでも知っている人なら誰で...
PR値、つまりPageRankは、Webページのレベル技術です。これは Google の創設者である...
2019年6月6日、非常に縁起の良い日に、工業情報化部は4つの主要通信事業者に5G商用ライセンスを発...
従来のシステムからクラウドベースのソリューションに部分的または完全に移行しようとする企業がますます増...
数カ月にわたるマイナス成長の後、美容・化粧品業界の企業は618を今年上半期の最後の命綱とみなしている...
月収10万元の起業の夢を実現するミニプログラム起業支援プラン読者数とクリック率を多く獲得できる魅力的...
以下は、Huizhou SEO Blog のために Ye Jianhui が書いたウェブサイト SE...