このテクノロジーは更新され、反復され続けるため、企業内では分散の概念がますます重視されるようになってきています。分散について話すとき、必ず分散ロックの話が出てきますが、現時点で分散ロックの実装方法としては Zookeeper
、DB
、 Redis
、の 3 つが主流です。この記事では例として Redis を使用します。
私たちの観点からすると、これら 3 つのプロパティは、分散ロックを効果的に使用するために必要な最低限の保証です。
安全機能: 相互に排他的です。常に 1 つのクライアントだけがロックを保持できます。
活力属性: デッドロックなし。最終的には、リソースをロックしているクライアントがクラッシュしたり分割された場合でも、ロックは常に取得できます。
アクティビティ: フォールト トレランス。 Redis ノードの大部分が実行されている限り、クライアントはロックを取得および解放できます。
Redis を使用してリソースをロックする最も簡単な方法は次のとおりです:
インスタンスにロックを作成します。
ロックは通常、Redis の有効期限機能を使用して限られた期間のみ存在するため、最終的には解放され、特定の期間が経過すると最終的には削除されます。
クライアントはリソースを解放する必要がある場合、ロックを削除します。
一見すると問題ないようです。しかし、詳しく見てみると、この実装スキームは Redis スタンドアロン環境では問題ないようです。しかし、ノードがダウンしたらどうなるでしょうか?さて、slave
ノードを追加しましょう。メインサーバーがダウンした場合は、このノードを使用してください。しかし、彼女が本当に対応できることが保証されているかどうかを見てみましょう?
この致命的な欠陥について話すときは、Redis レプリケーションが非同期であるという 1 つの知識ポイントを理解する必要があります。
クライアント A がメイン サーバーのロックを取得します。
マスターはロックのコピーをスレーブに転送する前にクラッシュしました。
スレーブ
に昇格
マスター
。
スレーブにはロックのオブジェクトがないため、クライアント B がロックを取得し、取得は成功しました。
これは明らかに間違っています。データを同期する前にマスター ノードがダウンしたため、スレーブ ノードにはデータがなく、分散ロックが失敗します。著者antirez
の視点は、これをどう解決するかということです。
著者は、複数の Redis
を使用する必要があると考えています。これらのノードは完全に独立しており、レプリケーションや調整のためのシステムを使用する必要はありません。複数の Redis システムのロックを取得するプロセスは次の手順になります:
現在のサーバー時刻をミリ秒単位で取得します
使用してみるロックを取得するには、同じキーとランダムな値を使用します。ロックを取得するときは、各マシンにタイムアウトが必要です。たとえば、ロックの有効期限が 10 秒の場合、単一ノード ロックを取得するためのタイムアウトは約 5 秒である必要があります。 50 ミリ秒。彼はこんな感じです。目的は、クライアントが障害が発生したマシンに確実に接続されるようにすることですが、これにより余分な時間が無駄になります。タイムアウト期間内にデータが取得されない場合、ノードは破棄され、すべてのノードが取得されるまで次のノードが取得されます。
取得が完了したら、現在の時刻から手順 1 で取得した時刻を引いた時刻を取得します。クライアントの半数以上が正常に取得し、ロックの取得時間が短縮された場合に限ります。ロック量のタイムアウト未満であれば、ロックが有効であることが証明されます。
ロックを取得した後のロック タイムアウトは次のとおりです。
設定された有効時間 - ロックを取得するのにかかる時間
#ロックを取得したマシンの半数以上が満足していない場合、またはロックが計算などの操作後にタイムアウトが負の場合、システムはすべてのインスタンスのロックを解除しようとします。一部のインスタンスがロックの取得に失敗した場合でも、ロックの解除が試行されます。
ロックを解放します。クライアントが指定されたインスタンスを正常にロックできると考えるかどうかに関係なく、すべてのインスタンスのロックを解放します。
Martin Kleppmann が記事タスク「Redlock はロックの安全性を保証できない!」を公開しました。
彼は、ロックの使用方法は 2 つだけだと考えています。
効率を向上させるには、ロックを使用して、タスクを 2 回実行する必要がないようにします。たとえば、(非常に高価な計算)
精度を確保するために、ロック メカニズムを使用して、タスクが通常のプロセス順序で実行されるようにし、2 つのノードが同じデータで動作するのを回避します。同時に、ファイルの競合とデータの損失が発生します。
第一の理由は、ロックに対して一定の許容度を持っているため、2 つのノードが同時に動作したとしても、システムへの影響は余分な労力だけです。計算コストがかからず、追加の影響はありません。現時点では、単一ポイントの Redis を使用することで問題をうまく解決でき、非常に多くの Redis インスタンスを保守してシステムの保守コストを増加させるために RedLock を使用する必要はありません。
ただし、2 番目のシナリオでは、金銭的な取引が含まれる可能性が高いため、より慎重になる必要があります。ロックが失敗した場合、および 2 つのノードの場合は、同じデータを同時に処理すると、ファイルの破損、データの損失、永続的な不整合、または金銭的損失が発生します。
2 つのクライアントがあるシナリオを想定します。各クライアントは、データをデータベースに保存する前にロックを取得する必要があります。RedLock アルゴリズムを使用して実装すると、どのような問題が発生しますか? RedLock では、デッドロックを防ぐために、ロックに有効期限が設定されていますが、Martin
は、これは安全ではないと考えています。フローチャートはこんな感じです!
クライアント 1 がロックの取得に成功した後、実行を開始しましたが、実行の途中でシステム内でフル GC が発生し、システム サービスが一時停止され、しばらくしてロックがタイムアウトしました。
クライアント 2 は、クライアント 1 のロックがタイムアウトするのを待った後、ロックを正常に取得し、ウェアハウジング操作の実行を開始しました。完了後、クライアント 1 はフル GC を完了し、別のウェアハウジング操作を実行しました。これは危険です!どうやって解決すればいいでしょうか?
Martin
は、オプティミスティック ロックに似た実装メカニズムを提案しました。図の例は次のとおりです。
システムにハングを引き起こす問題が発生した場合でも、データが正しく処理されることを保証するこのアイデアは完成したように見えます。しかし、考えてみてください:
A B C D E 5 つの Redis ノードがあるとします:
redisson
は、ロックの有効期限がビジネス プログラムの実行時間よりも絶対に長くなることを保証する A メカニズムを実装します。正式にはウォッチドッグ メカニズム (Watchdog) と呼ばれ、その主な原理は、プログラムがロックの取得に成功した後、子スレッドをフォークして、ロックが解放されるまで継続的にロックを更新することです。彼の概略図は次のようになります:
redisson はデーモン スレッドを使用してロックを更新します (デーモン スレッドの役割:
さらに、Redisson は RedLock アルゴリズム、フェア ロック、リエントラント ロック、チェーン、その他の操作も実装および最適化しているため、Redis 分散ロックの実装がより簡単かつ効率的になります。
以上がRedis を使用してリソースをロックする方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。