詳細な分析: Redis 分散ロックは安全ですか?

詳細な分析: Redis 分散ロックは安全ですか?

  [[413107]]

序文

Redis 分散ロックについては多くの記事が書かれていますが、インターネット上の記事の 99% ではこの問題が明確に説明されていないことがわかりました。その結果、多くの読者は多くの記事を読んでもまだ混乱しています。例えば、次の質問に明確に答えることができますか?

  • Redis に基づいて分散ロックを実装するにはどうすればいいですか?

  • Redis 分散ロックは本当に安全ですか?

  • Redis の Redlock の何が問題なのですか?それは間違いなく安全ですか?

  • 業界は Redlock について議論していますが、何について議論しているのでしょうか?どちらの見解が正しいでしょうか?

  • 分散ロックには Redis と Zookeeper のどちらを使用すればよいでしょうか?

  • 「フォールト トレラント」分散ロックを実装する際に考慮する必要がある問題は何ですか?

この記事では、これらの問題について徹底的に説明します。

この記事を読めば、分散ロックについて十分に理解できるだけでなく、「分散システム」についてもより深く理解できるようになります。

この記事は少し長いですが、役立つ情報がたくさん含まれています。気長に読んでいただければ幸いです。

1. 分散ロックが必要なのはなぜですか?

分散ロックについて話す前に、なぜ分散ロックが必要なのかを簡単に説明する必要があります。

分散ロックに対応するのが「単一マシンロック」です。マルチスレッド プログラムを作成する場合、共有変数を同時に操作することで発生するデータの問題を回避するために、通常は「相互排他」ロックを使用して共有変数の正確性を確保します。使用範囲は「同一プロセス内」となります。

複数のプロセスが同時に共有リソースを操作する必要がある場合、相互排他性をどのように実現できるでしょうか?

たとえば、現在のビジネス アプリケーションは通常、マイクロサービス アーキテクチャであり、1 つのアプリケーションで複数のプロセスが展開されます。これらの複数のプロセスが MySQL 内の同じレコード行を変更する必要がある場合、無秩序な操作によって発生するデータ エラーを回避するために、「分散ロック」を導入してこの問題を解決する必要があります。

分散ロックを実装するには、外部システムを使用する必要があり、すべてのプロセスがこのシステムで「ロック」を申請する必要があります。

この外部システムは「相互排他」機能を実装する必要があります。つまり、2 つの要求が同時に到着した場合、1 つのプロセスのみが成功を返し、もう 1 つのプロセスは失敗 (または待機) を返します。

この外部システムとしては、MySQL、Redis、Zookeeper などがあります。しかし、より良いパフォーマンスを追求するために、通常は Redis または Zookeeper を使用することを選択します。

次に、Redis をメインラインとして、浅いところから深いところまで、分散ロックのさまざまな「セキュリティ」の問題を詳細に分析し、分散ロックを徹底的に理解できるようにします。

2. 分散ロックを実装するにはどうすればいいですか?

一番簡単なものから始めましょう。

分散ロックを実装するには、Redis に「相互に排他する」機能が必要です。 SETNX コマンドを使用できます。これは、SET if Not eXists を意味します。つまり、キーが存在しない場合はその値が設定され、存在する場合は何も実行されません。

2 つのクライアント プロセスがこのコマンドを実行して相互排他を実現し、分散ロックを実装できます。

クライアント 1 がロックを申請し、ロックが成功します。

  1. 127.00.1 : 6379 > SETNX ロック1
  2. (整数) 1 // クライアント 1、正常にロックされました

クライアント 2 はロックを申請しますが、後から到着したため、ロックは失敗します。

  1. 127.00.1 : 6379 > SETNX ロック1
  2. (整数) 0 // クライアント 2、ロック失敗

この時点で、リソースを正常にロックしたクライアントは、「共有リソース」を操作できます。たとえば、MySQL のデータ行を変更したり、API リクエストを呼び出したりできます。

操作が完了したら、後から参加したユーザーが共有リソースを操作する機会を与えるために、ロックを時間内に解除する必要があります。ロックを解除するにはどうすればいいですか?

これも非常に簡単で、DEL コマンドを使用してこのキーを削除するだけです。

  1. 127.00.1 : 6379 > DEL lock // ロックを解除する
  2. (整数) 1

ロジックは非常にシンプルで、全体的な流れは次のようになります。

しかし、大きな問題があります。クライアント 1 がロックを取得するときに、次のシナリオが発生すると、「デッドロック」が発生します。

  • プログラムはビジネスロジック例外を処理し、時間内にロックを解除できません。

  • プロセスがハングし、ロックを解除する機会がない

この時点で、このクライアントは常にこのロックを占有し、他のクライアントは「決して」このロックを取得できなくなります。

この問題を解決するにはどうすればいいでしょうか?

3. デッドロックを回避するにはどうすればよいでしょうか?

簡単に考えられる解決策としては、ロックを申請する際に「リース期間」を設定することです。

Redis に実装する場合、このキーに「有効期限」を設定します。ここでは、共有リソースの操作時間が 10 秒を超えないと想定しているため、ロックするときにキーが 10 秒で期限切れになるように設定します。

  1. 127.00.1 : 6379 > SETNX ロック1 // ロック
  2. (整数) 1
  3. 127.00.1 : 6379 > EXPIRE lock 10 // 10秒後に自動的に期限切れになります
  4. (整数) 1

こうすることで、クライアントが異常かどうかに関わらず、10秒後にロックを「自動的に解除」することができ、他のクライアントは引き続きロックを取得できるようになります。

でも本当にこれでいいのでしょうか?

まだ問題が残っています。

現在の操作には、ロックと有効期限の設定という 2 つのコマンドが含まれます。最初のコマンドだけが実行され、2 番目のコマンドが時間内に実行されない可能性はありますか?例えば:

  • SETNX は正常に実行されましたが、ネットワークの問題により EXPIRE は失敗しました。

  • SETNXは正常に実行され、Redisは異常終了し、EXPIREは実行されません。

  • SETNXは正常に実行され、クライアントは異常終了し、EXPIREは実行されません。

つまり、これら 2 つのコマンドがアトミック操作 (一緒に成功する) であることは保証されず、有効期限の設定が失敗し、「デッドロック」問題が依然として発生する潜在的なリスクがあります。

何をするか?

Redis 2.6.12 より前では、SETNX と EXPIRE のアトミック実行を保証する方法を考え、さまざまな異常な状況に対処する方法も考慮する必要がありました。

しかし、Redis 2.6.12 以降では、Redis は SET コマンドのパラメータを拡張し、次のコマンドを使用できるようになりました。

  1. // 1つのコマンドでアトミック実行が保証されます
  2. 127.0 . 0.1 : 6379 > SETロック1 EX 10 NX
  3. わかりました

これによりデッドロックの問題が解決され、比較的簡単になります。

もう一度分析して、他にどんな問題があるか見てみましょう。

次のようなシナリオを想像してください。

  • クライアント1は共有リソースを正常にロックし、操作を開始します。

  • クライアント 1 が共有リソースの操作に費やす時間がロックの有効期限を超え、ロックは自動的に解除されます。

  • クライアント2は共有リソースを正常にロックし、操作を開始します。

  • クライアント 1 は共有リソース操作を完了し、ロックを解除します (ただし、解除されるのはクライアント 2 のロックです)

ご存知のとおり、ここには 2 つの重大な問題があります。

  • ロックの有効期限: クライアント 1 が共有リソースを操作するのに時間がかかりすぎるため、ロックが自動的に解放され、クライアント 2 によって保持されます。

  • 他の人のロックを解除する: クライアント 1 が共有リソースに対する操作を完了すると、クライアント 2 のロックが解除されます。

これら 2 つの問題の原因は何でしょうか?一つずつ見ていきましょう。

最初の問題は、共有リソースを操作する際の時間の評価が不正確であることから発生する可能性があります。

たとえば、共有リソースを操作するための「最も遅い」時間は 15 秒である可能性がありますが、有効期限を 10 秒しか設定していないため、ロックが早期に期限切れになるリスクがあります。

有効期限が短すぎるので、冗長時間を増やします。たとえば、有効期限を 20 秒に設定しますが、これで問題ないでしょうか。

確かに、これによって問題は「軽減」され、問題が発生する可能性は減りますが、それでも問題を「完全に解決」することはできません。

なぜ?

その理由は、クライアントがロックを取得した後、共有リソースを操作するときに発生するシナリオが、プログラム内の例外、ネットワーク要求のタイムアウトなど、非常に複雑になる可能性があるためです。

これは「推定」時間であるため、時間が長くなる原因となるすべてのシナリオを予測してカバーしない限り、大まかな計算しかできませんが、これは実際には困難です。

もっと良い解決策はあるでしょうか?

心配しないでください。対応する解決策については後で詳しく説明します。

2番目の質問に移りましょう。

2 番目の問題は、あるクライアントが別のクライアントによって保持されているロックを解放する場合です。

考えてみてください。この問題につながる重要なポイントは何でしょうか?

重要な点は、各クライアントが、ロックがまだ「自分自身によって所有されている」かどうかを確認せずに「無意識に」ロックを解除するため、他のユーザーのロックが解除されるリスクがあるということです。このようなロック解除プロセスは「厳密」ではありません。

この問題を解決するにはどうすればいいでしょうか?

4. 他人によってロックが解除された場合はどうすればよいですか?

解決策は、クライアントがデバイスをロックするときに、クライアントだけが知っている「一意の識別子」を設定することです。

たとえば、独自のスレッド ID または UUID (ランダムかつ一意) にすることができます。ここでは UUID を例に挙げます。

  1. // ロックのVALUEをUUIDに設定する
  2. 127.0 . 0.1 : 6379 > $uuid をロックする EX 20 NX
  3. わかりました

ここでは、操作共有時間は 20 秒で十分であると想定し、自動ロックの有効期限の問題は考慮しません。

後でロックを解除するときには、まずロックがまだ保持されているかどうかを確認する必要があります。疑似コードは次のように記述できます。

  1. // ロックはあなたのものなので、解放されます
  2. redis.get( "lock" ) == $uuidの場合:
  3. redis.del( "ロック" )

ここでは、ロックを解除するために GET + DEL の 2 つのコマンドが使用されます。このとき、先ほど説明した原子性の問題に遭遇します。

  • クライアント1はGETを実行して、ロックが自分のものであることを確認します。

  • クライアント2はSETコマンドを実行し、強制的にロックを取得します(発生確率は比較的低いですが、ロックのセキュリティモデルを厳密に考慮する必要があります)

  • クライアント1はDELを実行しますが、クライアント2のロックを解除します。

このことから、これら 2 つのコマンドはアトミックに実行する必要があることがわかります。

アトミックに実行するにはどうすればいいですか? Lua スクリプト。

このロジックを Lua スクリプトとして記述し、Redis に実行させることができます。

Redis は各リクエストを「シングルスレッド」方式で処理するため、Lua スクリプトを実行する場合、他のリクエストは Lua スクリプトが処理されるまで待機する必要があります。この方法では、GET + DEL の間に他のコマンドは挿入されません。

ロックを安全に解放するための Lua スクリプトは次のとおりです。

  1. // ロックを解除する前に、ロックが自分のものかどうかを判定する
  2. redis.call( "GET" 、KEYS[ 1 ]) == ARGV[ 1 ]の場合
  3. それから
  4. redis.call( "DEL" ,KEYS[ 1 ])を返す
  5. それ以外
  6. 0を返す
  7. 終わり

さて、この最適化により、ロックとロック解除のプロセス全体がより「厳密」になります。

ここではまず、Redis ベースの分散ロックについてまとめます。厳密なプロセスは次のとおりです。

  • ロック: SET lock_key $unique_id EX $expire_time NX

  • 共有リソースの運用

  • ロックを解除する: Lua スクリプト。まず GET でロックが自分のものかどうかを確認し、次に DEL でロックを解除します。

さて、この完全なロック モデルを使用して、先ほど述べた最初の質問に戻りましょう。

5. ロックの有効期限を予測するのが難しい場合はどうすればいいですか?

ロックの有効期限を予測するのが難しい場合はどうすればいいですか?

前述のように、ロックの有効期限が適切に見積もられていない場合、ロックが「早期に」期限切れになるリスクがあります。

当時提案された妥協案は、ロックが早期に期限切れになる可能性を減らすために、有効期限を可能な限り「冗長」にするというものでした。

この解決策では実際には問題を完全に解決することはできません。では、どうすればよいでしょうか?

次のようなソリューションを設計することは可能ですか: ロックするときに、まず有効期限を設定し、次に「ガーディアン スレッド」を開始して、ロックの有効期限を定期的に検出します。ロックの有効期限が近づいていて、共有リソースの操作が完了していない場合、ロックは自動的に「更新」され、有効期限がリセットされます。

これは確かにより良い解決策です。

Java テクノロジー スタックの場合は、幸いなことに、これらすべてのタスクをカプセル化するライブラリ、 Redisson がすでに存在します。

Redisson は、Java で実装された Redis SDK クライアントです。分散ロックを使用する場合、ロックの有効期限切れを回避するために「自動更新」ソリューションを採用します。このデーモン スレッドは一般に「ウォッチドッグ」スレッドと呼ばれます。

さらに、この SDK には、使いやすい機能が多数カプセル化されています。

  • 再入可能ロック

  • 楽観的ロック

  • フェアロック

  • 読み取り書き込みロック

  • レッドロック(詳細は下記)

この SDK によって提供される API は非常に使いやすいです。分散ロックをローカル ロックと同じように操作できます。 Java テクノロジー スタックを使用している場合は、それを直接使用できます。

ここではRedissonの使用については紹介しません。使い方は公式のGithubから学ぶことができ、比較的簡単です。

ここでは、Redis に基づく分散ロックの実装、以前に発生した問題、および対応する解決策をまとめます。

  • デッドロック: 有効期限を設定します。

  • 有効期限が適切に評価されず、ロックが早期に期限切れになる: ガーディアン スレッド、自動更新。

  • ロックが他のユーザーによって解除される: ロックには一意の識別子が書き込まれ、ロックを解除するときには、まず識別子がチェックされてから解除されます。

Redis ロックのセキュリティを危険にさらすその他の問題シナリオにはどのようなものがありますか?

これまで分析したシナリオはすべて、「単一の」Redis インスタンスをロックすることで発生する可能性のある問題に関するものであり、Redis デプロイメント アーキテクチャの詳細は関係ありませんでした。

Redis を使用する場合、通常はマスタースレーブ クラスター + Sentinelモードでデプロイします。これの利点は、マスター データベースが異常終了した場合に、Sentinel が「自動障害切り替え」を実装し、スレーブ データベースをマスター データベースに昇格させてサービスの提供を継続できるため、可用性が確保されることです。

では、「マスター/スレーブ切り替え」が発生した場合、この分散ロックは依然として安全でしょうか?

次のシナリオを想像してください。

  • クライアント 1 はマスター データベースで SET コマンドを実行し、正常にロックします。

  • この時点では、マスターデータベースは異常停止しており、SETコマンドはまだスレーブデータベースに同期されていません(マスタースレーブレプリケーションは非同期です)

  • スレーブ データベースはセンチネルによって新しいマスター データベースに昇格され、新しいマスター データベースのロックが失われました。

Redis レプリカの導入後も、分散ロックが影響を受ける可能性があることがわかります。

この問題を解決するにはどうすればいいでしょうか?

この目的のために、Redis の作者は、よく耳にする Redlock という解決策を提案しました。

上記の問題は本当に解決できるのでしょうか?

6. Redlock は本当に安全ですか?

さて、ようやくこの記事のハイライトにたどり着きました。ああ?上記の問題はすべて基本的なものなのでしょうか?

はい、これらは単なる前菜です。本当の味はここから始まります。

それでも上記の説明が理解できない場合は、もう一度読んで、ロックとロック解除の基本的なプロセスを整理することをお勧めします。

Redlock についてすでに何か知っている場合は、私をフォローしてここでもう一度確認することができます。 Redlock を知らない方も大丈夫です。もう一度ご案内します。

後で、Redlock の原則について話すだけでなく、「分散システム」に関連する多くの問題についても取り上げることを思い出す価値があります。私の考えに従って、頭の中にある質問に対する答えを分析したほうがよいでしょう。

ここで、Redis の作者が提案した Redlock ソリューションが、マスターとスレーブの切り替え後のロック失敗の問題をどのように解決するかを見てみましょう。

Redlock のソリューションは、次の 2 つの前提に基づいています。

  • スレーブセンチネルインスタンスを展開する必要はなくなり、マスターのみで済むようになりました。

  • ただし、複数のマスター データベースを展開する必要があり、公式の推奨数は少なくとも 5 つのインスタンスです。

つまり、Redlock を使用する場合は、少なくとも 5 つの Redis インスタンスをデプロイする必要があり、それらはすべてマスター データベースである必要があります。それらの間には関連性はなく、すべて独立したインスタンスです。

注: Redis クラスターをデプロイするか、5 つのシンプルな Redis インスタンスをデプロイします。

Redlock を具体的に使用するにはどうすればいいですか?

全体的なプロセスは次の 5 つのステップに分かれています。

  • クライアントはまず「現在のタイムスタンプT1」を取得します。

  • クライアントは、これらの 5 つの Redis インスタンスに対して順番にロック要求を開始し (上記の SET コマンドを使用)、各要求はタイムアウト (ミリ秒レベル、ロックの有効時間よりもはるかに短い) を設定します。特定のインスタンスがロックに失敗した場合 (ネットワーク タイムアウト、他のユーザーによるロックの保持、その他の異常な状況を含む)、次の Redis インスタンスがロックされるようになります。

  • クライアントが 3 つ以上の (最大) Redis インスタンスを正常にロックすると、「現在のタイムスタンプ T2」が再度取得されます。 T2 - T1 < ロック有効期限の場合、クライアントは正常にロックされたとみなされます。それ以外の場合は失敗したとみなされます。

  • ロックが成功し、共有リソースを操作できます(たとえば、MySQL の行を変更したり、API リクエストを開始したりできます)。

  • ロックが失敗した場合、ロック解除要求が「すべてのノード」に送信されます(上記の Lua スクリプトがロックを解除します)

簡単に要約させてください。重要なポイントは4つあります。

  • クライアントは複数のRedisインスタンスのロックを申請する

  • ほとんどのノードが正常にロックされていることを確認する必要があります

  • ほとんどのノードをロックするのにかかる合計時間は、ロックに設定された有効期限よりも短くなります。

  • ロックを解除するには、すべてのノードにロック解除要求を送信する必要があります。

初めて読んだときには理解しにくいかもしれません。記憶を深めるために、上記のテキストを数回読むことをお勧めします。

次に、これらの 5 つのステップを覚えておくことが非常に重要です。このプロセスに基づいて、ロック障害を引き起こす可能性のあるさまざまな問題の想定を分析します。

さて、Redlock のプロセスを理解したので、Redlock がなぜこれを実行するのかを見てみましょう。

1) 複数のインスタンスをロックする必要があるのはなぜですか?

本質的には、「フォールト トレランス」のためです。一部のインスタンスが異常クラッシュした場合でも、残りのインスタンスは正常にロックされ、ロック サービス全体が引き続き利用可能になります。

2) ほとんどのロックが成功した場合にのみ成功したと見なされるのはなぜですか?

複数の Redis インスタンスを一緒に使用すると、実際には「分散システム」が形成されます。

分散システムでは必ず「異常なノード」が存在するため、分散システムの問題を議論する際には、システム全体の「正確さ」に影響を与えずに、異常なノードがいくつ存在するかを考慮する必要があります。

これは分散システムにおける「フォールト トレランス」の問題です。この問題の結論は、「障害のある」ノードのみがある場合、ほとんどのノードが正常である限り、システム全体は引き続き正しいサービスを提供できるということです。

この問題のモデルは、よく耳にする「ビザンチン将軍問題」です。興味があれば、アルゴリズムの演繹プロセスを確認してください。

3) ステップ 3 でロックが正常に取得された後、ロックの累積時間を計算する必要があるのはなぜですか?

操作には複数のノードが関係するため、単一のインスタンスを操作する場合よりも確実に時間がかかります。また、ネットワークリクエストであるため、ネットワーク状況は複雑であり、遅延、パケット損失、タイムアウトなどが発生する可能性があります。ネットワークリクエストの数が多いほど、異常が発生する可能性が高くなります。

したがって、ほとんどのノードが正常にロックされたとしても、ロックにかかる累積時間がロックの有効期限を「超過」した場合、一部のインスタンスのロックはその時点で期限切れになっている可能性があり、ロックは無意味になります。

4) ロックを解除するにはなぜすべてのノードを操作する必要があるのですか?

Redis ノードをロックする場合、「ネットワーク上の理由」によりロックが失敗する可能性があります。

たとえば、クライアントが Redis インスタンスを正常にロックしたが、応答を読み取るときにネットワークの問題により読み取りが失敗した場合、ロックは実際には Redis 上で正常にロックされています。

したがって、ロックを解除するときは、以前にロックが正常に取得されたかどうかに関係なく、ノード上の「残留」ロックが確実にクリアされるように、「すべてのノード」のロックを解除する必要があります。

さて、Redlock プロセスと関連する問題について理解したところで、Redlock は Redis ノードの異常なダウンタイムによるロック障害の問題を実際に解決し、ロックの「セキュリティ」を確保しているようです。

しかし、これは本当にそうなのでしょうか?

7. レッドロック論争では誰が正しくて、誰が間違っているのでしょうか?

Redis の作者がこのソリューションを提案するとすぐに、業界の有名な分散システムの専門家から疑問の声が上がりました。

この専門家はマーティンと呼ばれ、英国ケンブリッジ大学の分散システムの研究者です。彼は以前、大規模なデータ インフラストラクチャに携わるソフトウェア エンジニア兼起業家でした。また、カンファレンスで頻繁に講演し、ブログを書き、本を執筆し、オープンソースの貢献者でもあります。

彼はすぐに、Redlock のアルゴリズム モデルの問題点を問う記事を書き、分散ロックの設計に関する独自の見解を述べました。

その後、疑問に直面したRedisの作者Antirez氏は負ける気はなく、相手の視点に反論し、Redlockアルゴリズムモデルの設計の詳細をさらに詳しく分析する記事も執筆しました。

さらに、この問題に関する議論は当時インターネット上でも非常に白熱した議論を引き起こしました。

二人は明確な考えと十分な議論を持っていた。これは、マスター同士の戦いであり、分散システムの分野における非常に良いアイデアの衝突でもありました。両者は分散システムの分野の専門家ですが、同じ問題に対して多くの相反する結論を出しています。何が起こっているのか?

以下、彼らの討論記事から重要な点を抜粋して紹介します。

注意: 以下の情報量は非常に多く、理解しにくい可能性がありますので、ゆっくり読むことをお勧めします。

分散型専門家マーティンがRelockに疑問を投げかける

彼は記事の中で主に以下の4つの議論を展開した。

1) 分散ロックの目的は何ですか?

マーティンは、まず分散ロックを使用する理由を理解する必要があると言いました。

彼は2つの目的があると考えています。

まず、効率性です。

分散ロックの相互排他機能は、同じ作業を不必要に 2 回実行すること (一部の高価なコンピューティング タスクなど) を回避するためのものです。ロックが失敗しても、電子メールが 2 回送信されるなどの「悪い」結果は発生せず、無害です。

2番目は、正確さです。

ロックは、同時実行プロセスが相互に干渉するのを防ぐために使用されます。ロックが失敗すると、複数のプロセスが同時に同じデータに対して操作を行うことになり、重大なデータ エラー、永続的な不整合、データ損失などの深刻な問題が発生します。それは患者に薬を繰り返し投与するようなもので、深刻な結果をもたらします。

前者、つまり効率性を求めるのであれば、Redis のスタンドアロン バージョンを使用すればよいと彼は考えています。ロック障害が時々発生した場合でも (ダウンタイム、マスター/スレーブ切り替え)、深刻な結果は発生しません。そして、Redlock を使用するのは重すぎて不必要です。

目標が正確性である場合、Redlock はセキュリティ要件をまったく満たしておらず、ロック失敗の問題が依然として残っていると Martin は考えています。

2) 分散システムでロックする際に発生する問題

マーティン氏は、分散システムは、想像もできないあらゆる種類の異常な状況を伴う複雑な「獣」のようなものだと語った。

これらの異常なシナリオには、主に 3 つの主要な領域 (分散システムが遭遇する 3 つの主要な山でもあるNPC) が含まれます。

  • N:ネットワーク遅延、ネットワーク遅延

  • P:プロセス一時停止、プロセス一時停止(GC)

  • C はクロックドリフト

Martin 氏は、プロセス一時停止 (GC) の例を使用して、Redlock のセキュリティ問題を指摘しました。

  • クライアント1はノードA、B、C、D、Eのロックを要求する

  • クライアント1がロックを取得した後、GCに入ります(これには長い時間がかかります)

  • すべてのRedisノードのロックが期限切れになりました

  • クライアント2はA、B、C、D、Eのロックを取得します。

  • クライアント1のGCが終了し、ロックが正常に取得されたとみなします。

  • クライアント2もロックを取得したと考え、「競合」が発生します。

Martin は、GC はプログラム内のどの時点でも発生する可能性があり、実行時間は制御できないと考えています。

注: もちろん、GC のないプログラミング言語を使用する場合でも、ネットワークの遅延やクロックのドリフトによって Redlock に問題が発生する可能性があります。ここで Martin は GC を例として使用しています。

3) 時計が正しいと仮定するのは不合理である

あるいは、複数の Redis ノードの「クロック」に問題が発生すると、Redlockロックが無効になる可能性があります。

  • クライアント1はノードA、B、Cのロックを取得しますが、ネットワークの問題によりDとEにアクセスできません。

  • ノードCのクロックが「前進」し、ロックが期限切れになる

  • クライアント 2 はノード C、D、E のロックを取得します。ネットワークの問題により、A と B にアクセスできません。

  • クライアント 1 と 2 は両方ともロックを保持していると認識します (競合)

マーティン氏は、Redlock は複数のノードのクロックの同期に「強く依存」しなければならないと考えています。ノードのクロックにエラーが発生すると、アルゴリズム モデルは無効になります。

C にクロック ジャンプがなく、代わりに「クラッシュ後すぐに再起動」された場合でも、同様の問題が発生します。

マーティンは、マシンの時計が間違っている可能性は十分にあると説明しています。

  • システム管理者が手動でマシンのクロックを変更した

  • NTP時間を同期する際にマシンクロックが大きく「ジャンプ」した

つまり、マーティンは、レッドロックのアルゴリズムは「同期モデル」に基づいていると考えています。多くの研究により、同期モデルの仮定は分散システムでは問題があることが示されています。

分散システムの混沌の中では、システム クロックが正しいと想定することはできないため、想定には細心の注意を払う必要があります。

4) 正確性を保証するフェーシングトークンソリューションを提案する

それに応じて、マーティンは分散ロックの正確性を確保するために、フェッキング トークンと呼ばれるソリューションを提案しました。

モデルのプロセスは次のとおりです。

  • クライアントがロックを取得すると、ロックサービスは「増分」トークンを提供することができる。

  • クライアントはこのトークンを使用して共有リソースを操作する

  • 共有リソースはトークンに基づいて「遅れて来た人」からのリクエストを拒否できる

このように、NPC にどのような異常事態が発生しても、分散ロックは「非同期モデル」に基づいて構築されているため、セキュリティが保証されます。

ただし、Redlock はフェッキングトークンと同様のソリューションを提供できないため、セキュリティを保証することはできません。

また、優れた分散ロックは指定された時間内に結果を出すことができるが、NPC がどのように発生しても間違った結果は出さないとも述べました。つまり、ロックの「パフォーマンス」(またはアクティビティ)にのみ影響し、「正確性」には影響しません。

マーティンの結論:

1. Redlock は、良い面も悪い面もあります。効率の点では、Redlock は重すぎて不要であり、正確性の点では、Redlock は十分に安全ではありません。

2.不合理なクロックの仮定:アルゴリズムは、システム クロックについて危険な仮定を行います (複数のノード マシンのクロックが一貫していると仮定します)。これらの仮定が満たされない場合、ロックは失敗します。

3.正確性を保証できない: Redlock はフェンシング トークンと同様のソリューションを提供できないため、正確性の問題を解決できません。正確性を保つには、Zookeeper などの「コンセンサス システム」を備えたソフトウェアを使用してください。

さて、上記は Redlock の使用に反対する Martin の見解であり、それは合理的であるように思われます。

次に、Redis の作者 Antirez がそれをどのように反論するかを見てみましょう。

Redis作者Antirezの反論

Redis の作者による記事には、3 つの重要なポイントがあります。

1) 時計の問題を説明する

まず、Redis の作者は、相手が提起した最も核心的な問題であるクロック問題を一目で理解しました。

Redis の作者は、Redlock では完全に一貫したクロックは必要なく、大まかに一貫しているだけでよく、「エラー」は許容されると述べています。

たとえば、5 秒を計測したいが、実際には 4.5 秒を記録し、その後 5.5 秒を記録する場合があります。ある程度の誤差はありますが、ロック失敗時間の「誤差範囲」を超えない限り、このクロック精度に対する要求はそれほど高くなく、実際の環境とも一致しています。

相手側が言及した「クロック変更」の問題について、Redis の作者は次のように反論しました。

クロックを手動で変更する: これを行わないでください。そうしないと、Raft ログを直接変更すると、Raft が動作しなくなります...

クロックのジャンプ: 「適切な操作とメンテナンス」により、マシンのクロックが大幅にジャンプしないようにすることができます (毎回小さな調整を行うことで実現します)。

Redis の作者はなぜクロックの問題の説明を優先したのでしょうか?なぜなら、その後の反論のプロセスでは、さらなる説明のためにこの根拠に頼る必要があるからです。

2) ネットワーク遅延とGCの問題を説明する

その後、Redis の作者は、ネットワークの遅延とプロセス GC によって Redlock が失敗する可能性があるという相手の質問にも反論しました。

マーティンが提起した疑問仮説を見直してみましょう。

  • クライアント1はノードA、B、C、D、Eのロックを要求する

  • クライアント1がロックを取得した後、GCに入る

  • すべてのRedisノードのロックが期限切れになりました

  • クライアント2はノードA、B、C、D、Eのロックを取得します。

  • クライアント1のGCが終了し、ロックが正常に取得されたとみなします。

  • クライアント 2 もロックを取得したと認識するため、「競合」が発生します。

Redis の作者は、この仮定は実際には問題があり、Redlock はロックのセキュリティを保証できると反論しました。

何が起こっているのか?

先ほど紹介した Redlock プロセスの 5 つのステップを覚えていますか?もう一度ここに持ってきて、レビューしてもらいます。

  • クライアントはまず「現在のタイムスタンプT1」を取得します。

  • クライアントは、これらの 5 つの Redis インスタンスに対して順番にロック要求を開始し (上記の SET コマンドを使用)、各要求はタイムアウト (ミリ秒レベル、ロックの有効時間よりもはるかに短い) を設定します。特定のインスタンスがロックに失敗した場合 (ネットワーク タイムアウト、他のユーザーによるロックの保持、その他の異常な状況を含む)、次の Redis インスタンスがロックされるようになります。

  • クライアントが 3 つ以上の (最大) Redis インスタンスを正常にロックすると、「現在のタイムスタンプ T2」が再度取得されます。 T2 - T1 < ロック有効期限の場合、クライアントは正常にロックされたとみなされます。それ以外の場合は失敗したとみなされます。

  • ロックが成功し、共有リソースを操作できます(たとえば、MySQL の行を変更したり、API リクエストを開始したりできます)。

  • ロックが失敗した場合、ロック解除要求が「すべてのノード」に送信されます(上記の Lua スクリプトがロックを解除します)

重要なポイントは1-3であることに注意してください。ステップ 3 では、ロックが正常に取得された後に「現在のタイムスタンプ T2」を再取得する必要があるのはなぜですか?ロックの有効期限と比較するために、T2 - T1 時間も使用しますか?

Redis の作者は、ネットワーク遅延やプロセス GC などの時間のかかる異常な状況が 1-3 で発生した場合、ステップ 3 T2 - T1 で検出できると強調しました。ロック設定の有効期限を超過した場合、ロックは失敗したとみなされ、すべてのノードのロックが解除されます。

Redis の作者は、相手側がステップ 3 以降にネットワーク遅延やプロセス GC が発生していると考えている場合、つまりクライアントがロックを取得したことを確認した後、共有リソースを操作する途中で問題が発生し、ロックが失敗する場合は、これは Redlock だけの問題ではなく、Zookeeper などの他のロック サービスでも同様の問題があり、議論の範囲外であると主張し続けました。

ここでこの問題を説明する例を挙げます。

  • クライアントは Redlock を通じてロックを正常に取得します (ほとんどのノードが正常にロックされ、ロック時間チェック ロジックが合格しました)

  • クライアントが共有リソースの操作を開始すると、ネットワークの遅延、プロセス GC などの時間のかかる状況が発生します。

  • この時点でロックは期限切れとなり、自動的に解除されます。

  • クライアントがMySQLの操作を開始する(この時点でロックが他のクライアントに取得され無効になる場合がある)

ここでの Redis 作者の結論は次のとおりです。

  • ロックを取得する前にクライアントがどのような時間のかかる問題に遭遇したとしても、Redlock はステップ 3 でそれを検出できます。

  • クライアントがロックを取得した後、NPCが発生し、RedlockとZookeeperは無力になります。

したがって、Redis の作者は、クロックの正確性を保証することに基づいて、Redlock が正確性を保証できると考えています。

3) フェンシングトークンの仕組みに疑問を呈する

Redis の作者も、相手側が提案したフェッキング トークンのメカニズムに疑問を呈しました。質問は主に2つあります。これが一番理解しにくいです。私の考えの流れに従ってください。

まず、このソリューションでは、運用する「共有リソース サーバー」に「古いトークン」を拒否する機能が必要です。

たとえば、MySQL を操作するには、ロック サービスから増分番号の付いたトークンを取得し、クライアントがこのトークンを使用して MySQL の行を変更する必要があります。これには、MySQL の「トランザクション分離」を使用する必要があります。

  1. // 両方のクライアントは、目的を達成するためにトランザクションと分離を使用する必要があります
  2. // トークン判定条件に注意する
  3. テーブル T を更新します。SET val = $new_val WHERE id = $id AND current_token < $token

しかし、MySQL を操作していない場合はどうなるでしょうか?たとえば、ファイルをディスクに書き込んだり、HTTP リクエストを開始したりする場合、このソリューションは無力となり、操作するリソース サーバーに高い要件が課せられます。

つまり、運用対象となるリソースサーバーのほとんどには、この排他制御機能がありません。

さらに、リソース サーバーには「相互排他」機能があるため、分散ロックは何の役に立つのでしょうか?

したがって、Redis の作者はこの解決策は受け入れられないと考えています。

第二に、Redlock がフェッチ トークン機能を提供しない場合でも、Redlock はすでにランダムな値 (前述の UUID) を提供しています。このランダム値を使用すると、フェッチトークンと同じ効果が得られます。

どうやってやるんですか?

Redis の作者は、トークンのフェッチと同様の機能を実行できることのみを言及しており、関連する詳細については説明していません。私が調べた情報によると、一般的なプロセスは次のようになるはずです。間違いがありましたら、お気軽にご連絡ください〜

  • クライアントはRedlockを使用してロックを取得します

  • 共有リソースを操作する前に、クライアントはまず操作する共有リソースにロック値をマークします。

  • クライアントはビジネス ロジックを処理します。最後に、共有リソースを変更するときに、タグが以前と同じかどうかを判断し、同じ場合のみ変更します(CASの考え方に似ています)

MySQL を例に挙げてみましょう。次に例を示します。

  • クライアントはRedlockを使用してロックを取得します

  • クライアントがMySQLテーブル内のデータ行を変更する前に、まず行内のフィールドのロック値を更新します(ここではcurrent_tokenフィールドであると想定しています)。

  • クライアントがビジネスロジックを処理する

  • クライアントは、VALUEをWHERE条件として使用してMySQLでこのデータ行を変更し、次に

  1. テーブル T を更新します。SET val = $new_val WHERE id = $id AND current_token = $redlock_value

このソリューションは、MySQL のトランザクション メカニズムに依存しており、相手側が言及したフェッキング トークンと同じ効果を実現していることがわかります。

しかし、ここでまだ小さな問題が残っており、議論に参加したネットユーザーによってこの問題が提起されました。このソリューションでは、2 つのクライアントが最初に共有リソースを「マーク」し、次に「チェック + 変更」するため、2 つのクライアントの操作順序は保証されません。

Martin が言及した料金トークンを使用すると、このトークンは単調に増加する数値であるため、リソース サーバーは小さなトークン要求を拒否し、操作の「連続性」を確保できます。

Redis の作者はこの問題について別の説明をしており、それは理にかなっていると思います。彼は次のように説明しました。「分散ロックの本質は「相互排除」です。」 2 つのクライアントが同時に実行された場合、一方が成功し、もう一方が失敗することが保証される限り、「連続性」について心配する必要はありません。

Martin さんの前回の質問では、常に順序の問題を懸念していましたが、Redis の作者は別の意見を持っています。

要約すると、Redis の作者の結論は次のとおりです。

1. 著者は、Redlock に対する「クロック ジャンプ」の影響については他の当事者と同意しますが、インフラストラクチャと運用および保守に応じてクロック ジャンプを回避できると考えています。

2. Redlock は NPC の問題を十分に考慮して設計されました。 Redlock のステップ 3 の前に NPC が発生した場合、ロックの正確性が保証されます。ただし、ステップ 3 以降に NPC が発生した場合、それは Redlock だけの問題ではなく、他の分散ロック サービスでも問題となるため、議論の範囲外となります。

面白いと思いませんか?

分散システムでは、小さなロックで多くの問題シナリオが発生し、セキュリティに影響する可能性があります。

両方の意見を読んだ後、あなたはどちらの意見にもっと同意するでしょうか?

心配しないでください。上記の議論を要約し、後で私自身の理解についてお話しします。

さて、Redis 分散ロックに関する両者の議論について説明しましたが、Martin は記事の中で、分散ロックの実装には Zookeeper を使用する方が安全だとして、それを推奨していることに気づいたかもしれません。これは本当ですか?

8. Zookeeper ベースのロックは安全ですか?

Zookeeperについて学んだ場合、それに基づいて実装された分散ロックは次のとおりです。

  • クライアント1と2は両方とも /ロックなどの「一時ノード」を作成しようとします

  • クライアント1が最初に到着すると仮定すると、ロックは成功し、クライアント2はロックに失敗します。

  • クライアント1は共有リソースを操作します

  • クライアント1は /ロックノードを削除し、ロックを解放します

また、ZookeeperはRedisのようにロックの有効期限を考慮する必要がないことに気づいたはずです。 「一時ノード」を使用して、クライアント1がロックを取得した後、接続が連続している限りロックを保持できることを確認します。

さらに、クライアント1が異常にクラッシュすると、一時的なノードが自動的に削除され、ロックがリリースされるようにします。

素晴らしい、ロックの有効期限について心配することはありません。例外の場合、ロックは自動的にリリースできます。完璧ではありませんか?

実はそうではありません。

それについて考えてみてください、クライアント1が一時的なノードを作成した後、Zookeeperはこのクライアントが常にロックを保持することをどのように保証しますか?

その理由は、クライアント1がこの時点でZookeeperサーバーとのセッションを維持し、このセッションは接続を維持するためにクライアントの「タイミングのハートビート」に依存するためです。

Zookeeper がクライアントのハートビートを長時間受信しない場合、セッションが期限切れであると見なし、一時ノードを削除します。

同様に、この問題に基づいて、Zookeeperロックに対するGCの問題の影響についても説明しましょう。

  • クライアント1は一時ノード/ロックを正常に作成し、ロックを取得します

  • クライアント1には長いGCがあります

  • クライアント1はZookeeperにハートビートを送信できないため、Zookeeperは一時ノードを「削除」します。

  • クライアント2は一時ノード/ロックを正常に作成し、ロックを取得します

  • クライアント1 GCは終了しますが、それでもロックを保持していると考えています(競合)

Zookeeperが使用されていても、異常なプロセスGCとネットワークの遅延があるシナリオではセキュリティを保証できないことがわかります。

これは、上記のRebuttalの記事でRedisの著者が述べたものです。クライアントがロックを取得したが、クライアントがロックサーバー(GCなど)との接続を失った場合、Redlockに問題があるだけでなく、他のロックサービスにも同様の問題があり、Zookeeperにも同じことが当てはまります。

したがって、ここで結論を描くことができます。分散ロックは、極端な場合に必ずしも安全ではありません。

ビジネス データが非常に機密性が高い場合は、分散ロックを使用する際にこの問題に注意する必要があり、分散ロックが 100% 安全であると想定することはできません。

さて、分散ロックを使用する場合の Zookeeper の利点と欠点をまとめてみましょう。

Zookeeper の利点:

  • ロックの有効期限を考慮する必要はありません

  • メカニズムを見る、ロックが失敗した場合、ロックが解放されるのを見て待つことができ、楽観的なロックを実現できます

しかし、その欠点は次のとおりです。

  • Redisほど良くありません

  • 高い展開と運用コスト

  • クライアントは長い間Zookeeperとのつながりを失い、ロックが解放されます

9。分布したロックについての私の理解

さて、Redisに基づいてRedlockとZookeeperによって実装された分散ロックと、さまざまな異常な状況でのセキュリティ問題を詳細に紹介しました。今、私の意見についてお話ししたいと思います。あくまで参考用です。あなたが同意しなければ私を批判しないでください。

1)レッドロックを使用する必要がありますか?

上記で分析したように、RedLockはクロックが正しい場合にのみ適切に機能します。この前提を保証できる場合は、使用できます。

しかし、時計が正しいことを確認するのは簡単だとは思わない。

まず、ハードウェアの観点から見ると、時計のオフセットが時々発生し、避けられません。

たとえば、CPU温度、機械荷重、チップ材料はすべて、クロック偏差を引き起こす可能性があります。

第二に、私の仕事の経験から、私はクロックエラーと運用とメンテナンスによる激しい時計の変更に遭遇しました。これは、システムの正確性に影響を与えました。したがって、人間の誤りも完全に回避することは困難です。

したがって、RedLockに対する私の個人的な意見は、あなたがそれを使用しないようにする必要があるということです。さらに、そのパフォーマンスはRedisのスタンドアロンバージョンほど良くなく、展開コストも高くなっています。マスタースレーブ +センチネルモードを使用して分散ロックを実装することを優先します。

正しさを確保する方法は? 2番目のポイントはあなたに答えを与えます。

2)分散ロックを正しく使用する方法は?

Martinの視点を分析するとき、彼はTokenソリューションを断ち切ることに言及し、それが私に多くのインスピレーションを与えてくれました。このソリューションには大きな制限がありますが、「正しさ」を確保するシナリオにとって非常に良い考えです。

したがって、2つを組み合わせることができます。

1.分散ロックを使用して、上層で「相互排除」の目的を達成します。ロックは極端な場合には故障しますが、最上層の同時リクエストを最大限にブロックし、動作リソースレイヤーへの圧力を減らすことができます。

2.ただし、絶対に正しいデータを必要とする企業の場合、「セーフティネット」をリソースレイヤーに提供する必要があります。デザインのアイデアは、fecingトークンソリューションを指すことができます。

2つのアイデアの組み合わせは、ほとんどのビジネスシナリオの要件を満たすことができると思います。

10. 結論

さて、要約させてください。

この記事では、Redisに基づいて実装された分散ロックが安全かどうかを主に説明します。

最も単純な分散ロックの実装から、さまざまな例外シナリオの処理、RedLockの導入、および2人の分散専門家の間の議論まで、RedLockの適用されるシナリオが終了しました。

最後に、Zookeeperが分散ロックを行うときに遭遇する可能性のある問題と、Redisとの違いも比較しました。

ここでは、これらのコンテンツをあなたの便宜のためにマインドマップに要約しました。

11。PostScript

この記事の情報の量は実際には非常に多いです。配布ロックの問題は徹底的に説明する必要があると思います。

あなたが理解していないなら、私はあなたがそれをさらに数回読んで、あなたの心の中でさまざまな想定されたシナリオを構築し、それについて繰り返し考えます。

この記事を書くとき、レッドロックの議論について2人の偉大なマスターによってこれらの2つの記事を読み直しました。私はたくさん得ました。ここでいくつかの経験をあなたと共有します。

1.分散型システム環境では、一見完璧なデザインはそれほど「厳格」ではないかもしれません。少し考えれば、さまざまな問題が見つかります。したがって、分散システムについて考えるとき、あなたは慎重で慎重でなければなりません。

2。レッドロックの議論から、私たちは正しいことや間違ったことにあまり注意を払うべきではありませんが、マスターが考えている方法と厳密に問題を精査するという厳格な精神についてもっと学ぶべきです。

最後に、レッドロックについて議論した後、マーティンによって書かれた洞察で終わります。

「私たちの前任者は私たちのために多くの大きな成果を生み出しました。巨人の肩の上に立って、私たちはより良いソフトウェアを構築できます。いずれにせよ、他の人からの詳細な精査に耐えることができるかどうかを議論して確認することで、それは学習プロセスの一部であることを確認します。

相互励まし。

<<:  カフカのこの「千里眼」について知っておく必要があります! ! !

>>:  サービス効率を向上させるにはどうすればよいでしょうか? Ruiyun Service Cloudはテクノロジーを活用して産業用ロボット業界を強化します

推薦する

ショッピングモールのウェブサイトの口コミマーケティングのやり方

B2C モールの運営と宣伝の過程で、多くのウェブマスターは、ウェブサイトのランキングを向上させ、宣伝...

金融会社が直面するクラウドコンピューティングの課題

金融サービス業界におけるデジタル変革の止められないペースは、おそらく他のどの業界よりも大きな混乱を引...

cmivps: 50% オフ、シアトル トライネットワーク AS4837 ライン VPS、20G 防御、年間 38 ドル、1G メモリ/1 コア/30g NVMe/2TB トラフィック、1G 帯域幅

cmivps は、米国シアトルのデータセンターで VPS のプロモーションを開始しました。この VP...

世界人工知能会議: AWS が人工知能と機械学習の新たなトレンドを明らかに

AWS は、2019 年の世界人工知能会議 (WAIC) で、Amazon Transcribe 自...

ファンを馬鹿扱いしないでください!セレブのWeiboマーケティング9つのタイプ

ショートビデオ、セルフメディア、インフルエンサーのためのワンストップサービスセレブリティWeiboマ...

ハイブリッドクラウド管理がIT戦略の成否を左右する

ピーター・ドラッカーは近代経営の父として知られ、企業経営に関する有名な洞察力で有名です。彼は著作の中...

将来の量子コンピューティングの7つの潜在的な用途を探る

量子コンピューティングの力を活用する組織は、人類が世界最大の問題のいくつかを解決し、医薬品研究から世...

VMware は VMware Tanzu および VMware Aria プラットフォームを強化し、顧客のニーズを満たす最新のアプリケーションの開発と提供を加速します。

今日の企業は、不安定なマクロ経済環境において回復力を獲得するために、ソフトウェアの俊敏性を優先する必...

SEO への基本的なルート: 2 つのポイントと 1 つのハート

SEO を効果的に行う方法は数多くあり、多くの SEO 担当者によって多くのテクニックがまとめられて...

raksmart - クリスマスセール: CN2 専用サーバー 32% オフ/G-port 無制限トラフィックサーバー 20% オフ

raksmart は、特別価格の独立サーバーをいくつか取り揃えたクリスマスと新年のプロモーションを実...

Xigua Video が Bilibili で UP ホストを追跡: お金は万能か?

BilibiliとXigua Videoの間で、新たな大Vsの攻防戦が始まった。表面的には、この競争...

クラウド アーキテクチャ DevOps を適用するには?

DevOps はプロセス改善に関連し、クラウド コンピューティングはテクノロジーとサービスの改善に関...

ビジネス戦争と紛争:ケータリングO2Oが世界を征服し、市場シェアをめぐる戦いを開始

O2Oは間違いなく中国の電子商取引市場におけるダークホースであり、近年の急速な拡大と成長は中国のO2...

ウェブサイト最適化競合分析

新しく引き継いだウェブサイトでも、競合他社の調査でも、SEO データ分析は不可欠です。ウェブサイト分...