序文今日は分散ロックについてお話します。インターネット上には関連コンテンツが多数存在しますが、それらは比較的散在しています。先ほど学習を終えて、それをまとめてみんなと共有しました。記事の内容は比較的長くなります。まずはマインドマップから議論する内容を理解しましょう。 写真 分散ロックとは何ですか?分散ロックは、相互排他によって一貫性を維持しながら、分散システム間の共有リソースへの同期アクセスを制御する方法です。 分散ロックを理解する前に、まずスレッド ロックとプロセス ロックを理解してください。 スレッド ロック: 主にメソッドとコード ブロックをロックするために使用されます。メソッドまたはコードがロックを使用する場合、一度にそのメソッドまたはコード セグメントを実行できるのは 1 つのスレッドだけです。スレッド ロックの実装は基本的に、Synchronized、Lock などのスレッド間の共有メモリに基づいているため、スレッド ロックは同じ JVM 内でのみ有効です。 プロセス ロック: 同じオペレーティング システム内の複数のプロセスを制御して、共有リソースにアクセスします。プロセスは独立しているため、各プロセスは他のプロセスのリソースにアクセスできません。したがって、プロセス ロックは同期スレッド ロックを通じて実装することはできません。 たとえば、Golang 言語の sync パッケージは、ミューテックス ロックなどの基本的な同期プリミティブを提供します。 ただし、上記の 2 つの方法は、モノリシック アーキテクチャ アプリケーションに適しています。ただし、分散システムでは、複数のサービス ノードと複数のプロセスが異なるノード マシンに展開されます。現時点では、リソースの競合により、ノードのローカル リソースをロックする上記の 2 つの方法は無効です。 このとき、分散システム内のリソースへの複数のプロセスによるアクセスを制御するには、分散ロックが必要です。したがって、分散ロックは分散排他問題を解決するために使用されます。 写真 分散ロックの特徴相互排除相互排他性は理解しやすいです。これも最も基本的な機能です。いつでも、ロックを取得できるのは 1 つのクライアントのみであり、2 つのクライアントが同時にロックを取得することはできません。 デッドロックの回避デッドロックはなぜ発生するのでしょうか?ロックを取得したクライアントが何らかの理由(ダウンタイムなど)でロックを解除できず、他のクライアントがロックを取得できなくなり、プロセス全体を続行できなくなるためです。 写真 もちろん、この状況には解決策があります! 有効期限の導入: 通常、デッドロックを回避するために TTL (Time To Live) を設定しますが、これを完全に回避することはできません。 1. 例えば、TTLが5秒の場合、プロセスAはロックを取得します 2. 問題は、プロセスAが5秒以内にロックを解除せず、システムによって自動的に解除され、プロセスBがロックを取得したことです。 3. ちょうど 6 秒目に、プロセス A は実行を終了し、ロックを解放します。つまり、プロセス A はプロセス B のロックも解放します。 有効期限を追加するだけでは、ロックの有効期限と他の人のロックの解放という2つの問題が発生します。 ロックの追加の一意性: 他の人のロックが解放される問題に対処するために、各クライアント プロセスに [一意の ID] を設定して、アプリケーション層で一意の ID を確認できるようにすることができます。 自動更新: ロックを保持する時間を正確に予測できないため、ロックの有効期限の問題が発生します。有効期限を短く設定しすぎると、期限が切れてしまうリスクがあります。ただし、有効期限を長く設定しすぎると、ロックが長時間解除されない可能性があります。 この状況に対処する方法もあります。デーモン (ウォッチドッグ) を起動して有効期限を検出し、リースを更新できます。たとえば、Java テクノロジー スタックでは、Redisson を使用してこれを処理できます。 再入可能性: スレッドがロックを取得したが、実行中に再度ロックを取得しようとするとどうなりますか? はい、ロックの取得が繰り返され、ロック リソースが占有され、デッドロックの問題が発生します。 「再入可能性」とは何かを理解しましょう。これは、同じスレッドがロックを保持しながらデッドロックを引き起こすことなく、ロックを複数回取得できることを意味します。つまり、スレッドはロックを取得した後、ロックが解放されるのを待たずに同じロックを再度取得することができます。 解決策: たとえば、Redis 分散ロックの再入可能性を実装するには、Redis の Lua スクリプト言語を使用し、参照カウンター テクノロジを使用して、同じスレッド上の再入可能ロックの正確性を確保する必要があります。 フォールトトレランスフォールト トレランスは、一部のノード (Redis ノードなど) がダウンした場合でも、クライアントがロックを取得および解放できることを保証します。一般的に言えば、これを処理するには 2 つの方法があります。 etcd/zookeeper のようなロック サービスの 1 つのタイプは、それ自体がクラスターであるため、自動的にフェイルオーバーを実行できます。もう一方のタイプは、複数の独立したロック サービスを提供できます。クライアントは複数の独立したロック サービスに要求を行います。ロック サービスが失敗した場合、他のサービスからロック情報を取得することもできます。しかし、これには明らかな欠点があります。クライアントは複数のロック サービスを要求する必要があります。 分類この記事では、分散ロックの 4 つの実装について説明します。実装方法に応じて、スピンとウォッチの2つのタイプに分けられます。 スピンモードデータベース ベースおよび Redis ベースの実装では、クライアントがロックの取得に失敗した場合、ループに入り、ロックの取得が成功するかタイムアウトするまで、ロックを取得できるかどうかを継続的に要求する必要があります。 監視方法この方法では、クライアントは特定のキーを監視するだけで済みます。ロックが利用可能になるとクライアントに通知されるため、クライアントは繰り返しリクエストする必要がありません。このメソッドは、zooKeeper および Etcd に基づいて分散ロックを実装するために使用されます。 実装分散ロックは、データベース、Redis キャッシュ、ZooKeeper など、さまざまな方法で実装できます。この記事では、主にこれらの実装方法を問題解決方法と組み合わせて説明します。 MySQLベースデータベース テーブルを使用して分散ロックを実装することに少し戸惑いを感じますか?はい、私も執筆前に情報収集しているときに疑問に思ったことはありました。この方法はお勧めできませんが、解決策として理解することもできます。どのように行われるか見てみましょう: たとえば、メソッド名などのフィールドを含むテーブルをデータベースに作成し、メソッド名フィールドに一意のインデックスを作成します。メソッドを実行する場合は、このメソッド名を持つレコードをテーブルに挿入します。挿入が成功するとロックが取得され、対応する行を削除するとロックが解除されます。 ここでは、名前フィールドが主に一意のインデックスとして使用されます。一意のインデックスにより、レコードの一意性が保証されます。ロックが解除されると、レコードを直接削除できます。 欠点もたくさんあります:
ここではあまり多くのテキストは必要ありません。実際には、分散ロックを実装するために、より多くのメモリベースのストレージを使用します。 Redisベースインタビュアーは質問しました: 分散ロックについて理解していますか?ほとんどの面接官は、Redis が分散ロックをどのように実装するかについて話すと思います。さて、本題に入りましょう[Redis ベースの分散ロック]
通常の状況では可能ですが、ここで問題があります。 setnx はアトミックですが、setnx + expire はアトミックではありません。つまり、setnx と expire は 2 つのステップで実行され、[ロックとタイムアウト] の 2 つの操作は別々です。 expire の実行に失敗した場合、ロックは解除されません。 ロックとタイムアウト設定の理由については、記事の冒頭[デッドロックの回避]に記載されています。理解できない場合は、詳細を読んでください。
randId はクライアントによって生成されるランダムな文字列です。クライアントがロックするときに一意であり、主に他の人のロックが解除されるのを防ぎます。 以下に示すように、同じプロセスを見てみましょう。 写真
この randId は、ロックを解除するときに他の人のロックが解除されることを回避できます。ロックを解除するときに、クライアントはまずロックの値 (randId) を取得し、削除する前にそれらが同じかどうかを判断する必要があるためです。 ロック時にはアトミック性が求められますが、ロックを解除するときにアトミック性を実現するにはどうすればよいでしょうか? これは良い質問です。ロックするときは、有効期限の設定で潜在的な失敗を回避するためにアトミック コマンドを使用します。ロックを解除するには、Get + Del という 2 つのコマンドも必要であり、他の人のロックも解除するという問題もあります。 頭がぐるぐるしています😵、なぜこんなに考慮すべきことがたくさんあるのでしょうか?読むのに疲れたので、ちょっと休憩します😴、それから読み続けます! ここでの問題の根本は、次の図に示すように、ロックがクライアント側で決定され、サーバー側で解除されることです。 写真 そのため、ロックの判定と削除はRedisサーバー上で行う必要があります。 Lua スクリプトを使用すると、アトミック性を確保し、ロックのコアロジック [GET、判定、DEL] を解除し、それを Lua スクリプトに記述して、Redis に実行させることができます。この実装により、これら 3 つのステップのアトミック性が保証されます。
業務実行時間がロック有効期限を超えたため、ロックを更新することができます。たとえば、デーモン プロセスを開始して、ロックの有効期限を定期的に監視することができます。ロックの有効期限が切れそうになったら、自動的にロックを更新し、有効期限をリセットすることができます。 これは、WatchDog を必要とする Redisson フレームワークに実装されています。ロック時にロック時間が指定されていない場合、ウォッチドッグ メカニズムが有効になります。デフォルトのロック時間は 30 秒で、10 秒ごとにチェックされます。存在する場合、有効期限は 30 秒にリセットされます (つまり、30 秒後には更新されません) 写真 はい、これでもっと安定するはずです! 😋 ええと、上記のすべては、「単一の」Redis インスタンスをロックすることによって発生する可能性のある問題です。実際、単一ノードの分散ロックはほとんどの人々のニーズを解決できます。しかし、Redis の高可用性は通常、[Redis Cluster] または [Sentinel Mode] を使用して実現されますが、これによりマスターとスレーブの同期の問題が発生します。 😭 次のシナリオを想像してください。
写真 この問題に直面して、Redis の作者は、複数の Redis ノード (すべてマスター) に基づく実装である Redlock という解決策を提案しました。このソリューションは、次の 2 つの前提に基づいています。
Redlock のロック プロセス: 1. クライアントはまず「現在のタイムスタンプT1」を取得します。 2. クライアントは、これらの 5 つの Redis インスタンスに対して順番にロック要求を開始し (上記の SET コマンドを使用)、各要求はタイムアウト (ミリ秒レベル、ロックの有効時間よりもはるかに短い) を設定します。特定のインスタンスがロックに失敗した場合 (ネットワーク タイムアウト、他のユーザーがロックを保持している場合など)、次の Redis インスタンスに直ちにロックが適用されます。 3. クライアントが 3 つ以上の (最大) Redis インスタンスから正常にロックすると、「現在のタイムスタンプ T2」が再度取得されます。 T2 - T1 < ロック有効期限の場合、クライアントは正常にロックされたとみなされ、それ以外の場合は失敗したとみなされます。 4. ロックが正常に取得されたら、共有リソースを操作します(たとえば、MySQLの行を変更したり、APIリクエストを開始したりします)。 5. ロックが失敗し、クライアントは「すべてのノード」にロック解除要求を送信します(上記の Lua スクリプトがロックを解除します) Redlock リリースロック: クライアントはすべての Redis ノードでロック解除操作を開始します。 写真
基本的に、フォールト トレランスのために、図の複数のマスター サンプル ノードが実際に分散システムを構成していることがわかります。分散システムでは異常なノードが常に存在します。複数のインスタンスがロックされている場合、一部のインスタンスが異常終了しても、残りのインスタンスは正常にロックされ、ロック サービス全体が引き続き利用可能になります。
ロック操作は分散システム内の複数のノードを対象とするため、単一インスタンスよりも時間がかかります。ネットワークの遅延、パケット損失、タイムアウトなども考慮する必要があります。ネットワーク要求が増えるほど、例外が発生する可能性が高くなります。 したがって、N/2+1 個のノードが正常にロックされたとしても、ロックにかかる累積時間がロックの有効期限を超えた場合、その時点でのロックは無意味になります。
主な目的は、ノードの異常によって発生した残留ロックを確実にクリアすることです。 たとえば、Redis ノードをロックする場合、「ネットワーク上の理由」によりロックが失敗する可能性があります。 または、クライアントが Redis インスタンスを正常にロックしたが、応答を読み取るときにネットワークの問題により読み取りが失敗した場合、ロックは実際には Redis 上で正常にロックされています。 したがって、ロックを解除するときは、以前にロックが正常に取得されたかどうかに関係なく、すべてのノードのロックを解除する必要があります。 Redlock のセキュリティについては議論がありますが、ここでは簡単に触れておきます。ご興味がございましたら、ぜひご覧ください: Java インタビュー 365: RedLock セキュリティ論争 (パート 1) 4 件のいいね · 0 件のコメント 写真 Etcdに基づくEtcd は、Go 言語で実装された非常に信頼性の高いキー値ストレージ システムです。多くの場合、分散システムに重要なデータを保存し、通常は構成センター、サービスの検出と登録、分散ロックなどのシナリオで使用されます。 この記事では、分散ロックの観点から、Etcd が分散ロックをどのように実装するかを主に説明します。さあ行こう! Etcd の機能:
これらの機能により、Etcd が分散ロックを実装できるのはなぜですか? Etcd のこれらの機能は、分散ロックを実装するための次の要件を満たすことができるためです。
この知識と理論に基づいて、Etcd が分散ロックをどのように実装するかを見てみましょう。私も Golang 開発者なので、ここにもいくつかコードを載せておきます。 まずプロセスを見て、それをコードコメントと組み合わせてください。 写真
ただし、clientv3 によって提供される同時実行パッケージは分散ロックも実装します。分散ロックをより便利に実装できますが、内部実装ロジックは同様です。
ZooKeeper をベースにZooKeeper のデータ ストレージ構造はツリーのようなもので、Znode と呼ばれるノードで構成されています。 ロックのロック/解除のプロセスは次のとおりです。 写真 1. クライアントは /lock などの znode を作成しようとします。 Client1 が最初に到着すると、znode が正常に作成され、ロックを取得するのと同じになります。 2. 他のクライアントは作成に失敗し (znode が既に存在する)、ロックを取得できません。 3. クライアント2は待機状態に入り、/lockノードが削除されたときにZooKeeperが監視メカニズムを通じて通知するのを待つことができます。 4. ロックを保持しているクライアント1が共有リソースへのアクセスを完了すると、znodeを削除してロックを解除します。 5. クライアント2はロックが取得されるまでロック取得操作を完了し続ける。 ZooKeeper では有効期限を考慮する必要はなく、[一時ノード] を使用します。クライアントがロックを取得すると、接続が継続している限り、常にロックを保持します。クライアントがクラッシュした場合でも、ロックが確実に解除されるように、対応する一時ノード Znode が自動的に削除されます。
各クライアントは ZooKeeper とのセッションを維持し、定期的なハートビートによって維持されます。 Zookeeper がクライアントのハートビートを長時間受信しない場合、セッションが期限切れであると見なし、一時ノードを削除します。 もちろん、これは完璧な解決策ではありません。 次のシナリオでは、クライアント 1 とクライアント 2 がウィンドウ時間内に同時にロックを取得する可能性があります。 1. クライアント 1 は znode /lock を作成し、ロックを取得します。 2. クライアント 1 は長時間の GC 一時停止に入ります。 (または、ネットワークに問題があるか、zk サービス検出ハートビート スレッドに問題があるなど) 3. ZooKeeper に接続しているクライアント 1 のセッションが期限切れになりました。 znode/ロックは自動的に削除されます。 4. クライアント 2 は znode /lock を作成し、ロックを取得します。 5. クライアント 1 は GC 一時停止から回復し、まだロックを保持していると認識します。 さて、分散ロックを使用する場合の Zookeeper の利点と欠点をまとめてみましょう。 Zookeeper の利点:
欠点:
要約するこの記事には多くの内容が含まれており、多くの知識ポイントが含まれています。一度読んでも理解できない場合は、保存して何度か読み、分散ロックのシナリオ構造を構築することをお勧めします。 まとめると、この記事では主に分散ロックとその使用方法についてまとめています。分散ロックを実装する方法は多数あります。 データベース: ロックは一意のレコードを作成することによって表されます。一意のレコードが正常に追加されると、ロックが正常に作成されます。ロックを解除するにはレコードを削除する必要がありますが、パフォーマンスのボトルネックが発生する可能性があります。したがって、データベースは基本的に分散ロックとしては使用されません。 Redis : Redis はロックの取得と解放の効率的な操作を提供し、Lua スクリプト、Redission などと組み合わせることで、例外を処理するためのより優れた方法を備えています。メモリベースなので、読み書き効率も非常に高いです。 Etcd : リース、ウォッチ、リビジョンのメカニズムを使用して、シンプルな分散ロック方式を提供します。クラスター モードでは、Etcd は優れたパフォーマンスで大量の読み取りと書き込みを処理できますが、構成が複雑で一貫性の問題も存在します。 Zookeeper : ZooKeeper が提供するノード同期機能を使用して、有効期限を設定せずに分散ロックを実装し、異常な状況でのロック解除を自動的に処理できます。 ビジネス データが非常に機密性が高い場合は、分散ロックを使用する際にこの問題に注意する必要があり、分散ロックが 100% 安全であると想定することはできません。 もちろん、自社のビジネスと組み合わせることも必要です。ほとんどの場合、Redis を分散ロックとして使用する可能性があります。理由の一つは、私たちにとってより馴染み深いものであり、パフォーマンスの実行方法や異常事態への対処方法が多数あるからです。ほとんどのビジネスシナリオに対応できると思います。 |
<<: ビッグデータクラウドネイティブの発展の道筋をどう見るか - 2023年雲奇カンファレンスの考察
>>: ファーウェイクラウド、業界インテリジェンスのアップグレードを加速する初の大規模ハイブリッドクラウドを発表
O2Oモデルが登場した後、この豪雨は数百軒の家屋を倒壊させ、数千本の苗木を折った。生き残り、その渦の...
IDCは、中国のエッジコンピューティングサーバーの市場規模全体が2020年から2025年にかけて年間...
2月に「青大根アルゴリズム」がリリースされて以来、多くのウェブマスターがBaiduがリリースしたこの...
業界にとって、データの流れは大きな変化を遂げています。変化の背景には、5Gやモノのインターネットの潮...
1. SuningがRedbabyのトップ経営陣を刷新。次のRedbabyは誰になるのか?レッドベイ...
ソファーにつかまって、投稿をフォローして、トップに立って、降りる...これらは、インターネットの新時...
Godaddy は長い間何も活動していないようです。もちろん、国内ユーザーが参加できるものについて話...
iAskは設立から2ヶ月が経ち、毎日定期的に外部リンクが追加されています。Baiduには1,070の...
最近、A5 Webmaster Network で、10 日間で 7 の重みを獲得した斬新な Web...
実は、SEOerの育成や将来という話題は、多くの人から言及されています。私が今日このような話題を繰り...
Expertvm は 2011 年に設立され、シンガポールの SecureAX のサブブランドです。...
過去 10 年間の IoT (モノのインターネット) の拡大、5G の導入、エッジ コンピューティン...
現在、デジタル経済は世界経済の質の高い発展を促進するための重要な原動力となっています。データは中核的...
digitalvirt は、ロサンゼルス データ センターで China Unicom AS9929...
高画質携帯電話、無人航空撮影、VRなどのハードウェア技術の進歩、5Gなどのインフラの普及により、映像...