Redis アプリケーション (Stars Chasing the Moon): 分散ロック

Redis アプリケーション (Stars Chasing the Moon): 分散ロック

[[431326]]

トピックの紹介

みなさんこんにちは、シャオロンです。

以前、「Redis をマスターするシリーズ」コラムの最初の記事「Redis の基礎 (基礎から始まる高層ビル): コアとなる基礎データ構造」を公開し、Redis、その内部構成、コアとなるデータ構造、一般的な使用シナリオについて簡単に紹介しました。まだ見ていない生徒は戻って見ることができます。

次回も引き続き、より深い理解へと導いてまいります。この記事では、Redis がよく使用されるシナリオ「Redis を使用して分散ロックを実践する」を紹介します。

同時実行の問題に遭遇した場合、通常、同時実行の問題を解決するためにロックを使用することは誰もが知っていると思います。

この時点で、生徒の中には「これはわかっている。 synchronized と Lock を使って実装すればいいのでは?」と言う人もいるかもしれません。

はい、その通りです。しかし、あなたの言うことは半分しか正しくありません。 「従来のスタンドアロン展開」の場合、排他制御には Java の並行処理関連の API (ReentrantLcok や synchronized など) を使用できます。

しかし、「分散システム」では、分散システムの「マルチスレッド」、「マルチプロセス」、「異なるマシンへの分散」により、元の単一マシン同時実行制御ロック戦略は無効になります。この問題を解決するには、分散ロックに依存する共有リソースへのアクセスを制御する「JVM 間排他メカニズム」が必要です。

ロックの本質を見抜く

私の意見では、すべてのロック自体は変数で表すことができます。

たとえば、「単一のマシン」上で実行されるマルチスレッド プログラムなどです。変数を取得します。変数が 0 の場合、どのスレッドもロックを取得していないことを意味します。変数が 1 の場合、スレッドがロックを取得したことを意味します。

ロック: スレッドはロック操作を呼び出し、変数が 0 かどうかを確認します。0 の場合は、どのスレッドもロックを取得していないことを意味します。ロックが取得されたことを示すために、変数は 1 に設定されます。 0 以外の場合は、他のスレッドが一時的にロックを使用していて、ロックを取得できなかったことを意味します。

ロック解除: 上記と同じ。

分散環境では、分散ロックは変数の形式でも理解できます。

ただし、単一のマシン上でロックを操作するスレッドとは異なり、分散シナリオでは、「ロック変数は共有ストレージ システムによって維持される必要があります。」この方法でのみ、複数のクライアントが共有ストレージ システムにアクセスしてロック変数にアクセスできます。それに応じて、「ロックのロックと解除の操作は、共有ストレージ システム内のロック変数値の読み取り、判断、および設定になります。」

「分散ロックの要件が満たされていることがわかります」:

  • 「ロック操作の原子性」: 分散ロックをロックおよび解放するプロセスには、複数の操作が含まれます。したがって、分散ロックを実装する場合は、これらのロック操作のアトミック性を確保する必要があります。
  • 「ロックの信頼性」: 共有ストレージ システムはロック変数を保存します。共有ストレージ システムに障害が発生したりクラッシュしたりすると、クライアントはロック操作を実行できなくなります。分散ロックを実装する場合、「共有ストレージシステムの信頼性」の確保を考慮した上で、「ロックの信頼性」を確保する必要があります。

上で、ロックを表すためにロック変数を使用できることを説明しましたが、これは「プレースホルダー」と考えることもできます。分散ロックでは、このピットを取り出して「共有」の場所に置く必要があり、全員が「共有」の場所からピットをチェックするだけです。

プレースホルダーは通常、setnx (存在しない場合は設定) 命令を使用して作成され、1 つのクライアントのみがその場所を占有できるようになります。先着順です。使用が終わったら、del コマンドを呼び出してトイレを解放します。

  1. //ロック
  2. > setnx ロックキー 1
  3. わかりました
  4. //ビジネスロジック
  5. >(その他の操作)
  6. //ロックを解除する
  7. > del lock_key

しかし、問題があります。ロジック実行の途中で例外が発生した場合、del 命令が呼び出されず、「デッドロック」が発生し、ロックが解放されなくなります。

したがって、ロックを取得した後、ロックに有効期限を追加します。これにより、途中で例外が発生した場合でも、指定された時間後にロックが自動的に解除されることが保証されます。

  1. //ロック
  2. > setnx ロックキー 1
  3. わかりました
  4. > ロックキー 5 の有効期限
  5. //ビジネスロジック
  6. >(その他の操作)
  7. //ロックを解除する
  8. > del lock_key

しかし、上記のロジックにはまだ問題が残っています。マシンの電源が切れたり手動で強制終了されたりしたために、setnx と expire の間でサーバー プロセスが突然クラッシュした場合、expire は実行されず、デッドロックが発生する可能性もあります。

この問題の根本は、setnx と expire がアトミック命令ではなく 2 つの命令であることです。トランザクションなどを使用して実行することも考えられますが、setnx がロックを取得しない場合は、expire は実行されないため、ここでは機能しません。

Redis 2.8 では、set コマンドに拡張パラメータが追加され、setnx コマンドと expire コマンドを一緒に実行できるようになり、分散ロックの問題が完全に解決されました。

  1. セット キー値 [EX 秒 | PXミリ秒] [NX]

上記の基本的な日常的な質問に加えて、次のような「考慮していない可能性のある質問」もあります。

タイムアウトの問題

Redis の分散ロックではタイムアウトの問題を解決できません。ロックとロック解除の間のビジネス ロジック実行時間が長すぎて、ロック タイムアウト制限を超えると、問題が発生します (つまり、ロックが期限切れになり、ビジネス ロジックが実行されません)。

この時点でロックの有効期限が切れているため、2 番目のクライアント B は再びロックを保持しますが、クライアント A がビジネス ロジックを実行するとすぐにロックが解除されます。クライアント B がロジックの実行を完了する前に、クライアント C がロックを取得します。この問題を回避するには、時間のかかるタスクには Redis 分散ロックを使用しないでください。

この問題に対処するには、異なるクライアントからのロック操作を区別できる必要があります。どうすればこれができるでしょうか?この問題に対処するには、コマンドにいくつかのトリックを追加する方法を見つけることができます。変数の値をロックする方法を考えることができます。

SETNX コマンドを使用してロックする方法では、ロックが成功したかどうかを示すためにロック変数の値を 1 または 0 に設定します。状態は 1 と 0 の 2 つだけであり、どのクライアントがロック操作を実行したかを示すことはできません。

したがって、ロック操作を実行するときに、「各クライアントにロック変数の一意の値を設定させる」ことができます。ここでの一意の値を使用して、現在の操作のクライアントを識別できます。

ロックを解除する場合、クライアントは現在の「ロック変数の値が自身の一意の識別子と等しいかどうか」を判断する必要があります。それらが等しい場合にのみロックを解除できます。こうすることで、誤ってロックを解除してしまうという問題がなくなります。

したがって、コマンドは次のように記述できます。

  1. //ロック、unique_valueはクライアントの一意の識別子として使用されます
  2. ロックキーの一意の値を設定するNX PX 5000

このうち、unique_value はクライアントの一意の識別子であり、ランダムに生成された文字列で表すことができます。 PX 5000 は、この期間中に例外が発生してクライアントがロックを解除できなくなることを防ぐために、lock_key が 5 秒後に期限切れになることを意味します。

各クライアントはロック操作で一意の識別子を使用するため、ロックを解除するときには、ロック変数の値がロック解除操作を実行するクライアントの一意の識別子と等しいかどうかを判断する必要があります。以下に示すように、Lua スクリプトを使用してアトミック性を確保できます。

  1. //ロックを解除し、誤って解除されないように unique_value が等しいかどうかを比較します
  2. redis.call( "get" ,KEYS[1])== ARGV[1]の場合 
  3. redis.call( "del" , KEYS[1])を返す
  4. それ以外 
  5. 0を返す
  6. 終わり 

再入性

再入可能性とは、スレッドがロックを保持したまま再度ロックを要求できることを意味します。ロックが同じスレッドによる複数のロックをサポートする場合、そのロックは再入可能になります。たとえば、Java 言語には再入可能ロックである ReentrantLock があります。

Redis 分散ロックが再入可能性をサポートする必要がある場合、クライアントの set メソッドをパッケージ化し、スレッドのスレッドローカル変数を使用して現在保持されているロックの数を保存できます。

ほとんどの人は尋ねないので、ここではあまり詳しく説明しません。興味があれば、ネットで調べたり、本を読んだりしてみてください。

課外活動の補足

上記の内容は、単一の Redis ノードに基づいて実装された分散ロックです。

「信頼性の高い分散ロック」を実装する場合、単一のコマンド操作だけに頼ることはできません。ロックおよびロック解除操作を実行するには、特定の手順とルールに従う必要があります。そうしないと、ロックが機能しない可能性があります。 「特定の手順とルール」とはどういう意味ですか?実は、これは分散ロックのアルゴリズムです。

ここでは、Redlock アルゴリズムの実行手順について簡単に説明します。 Redlock アルゴリズムの実装には、N 個の独立した Redis インスタンスが必要です。次に、3 つのステップでロック操作を完了します。

1. クライアントは現在時刻を取得する

2. クライアントは、各マスターインスタンスで順番にロックを取得しようとします。ロックを取得するプロセスでは、各ロック操作の失敗時間を短く設定します (10 秒のロックを取得する場合、各ロック操作の失敗時間は 5 ~ 50 ミリ秒に設定されます)。

これにより、クライアントが障害の発生したマスターと通信するのに時間がかかりすぎるのを防ぎ、高速障害を通じてクラスター内の他のノードとのロック操作をできるだけ早く完了することができます。

3. クライアントは、マスターとのロック取得プロセスで消費された時間を計算します。ただし、「クライアントがロックを取得するために消費した時間がロックの存続時間よりも短く、マスター ノードの半分以上でロックが取得された場合に限ります。」その場合にのみ、クライアントがロックを正常に取得したとみなされます。

4. ロックが取得された場合、「クライアントがタスクを実行するための時間枠は、ロックの存続時間からロックの取得に費やされた時間を差し引いた時間になります。」

5. クライアントが取得したロック数が半分未満の場合、またはロック取得時間がタイムアウトになった場合は、ロック取得に失敗したとみなされます。クライアントは、「2 番目のステップでマスター ノードのロックを正常に取得できなかった場合でも、すべてのマスター ノードのロックを解除しようとする必要があります。」

<<:  JVM の全体的な構造、実行プロセス、および 2 つのアーキテクチャ モデルの図解による説明。学びましたか?

>>:  疑似 SaaS ビジネス モデルがなぜこれほど普及しているのでしょうか?

推薦する

この記事を読むまで私はカフカのことをよく知っていると思っていた

Kafka は、もともと LinkedIn で開発されたメッセージング システムであり、Linked...

調査によると、オンラインでの共有と情報過多は世界的な問題である

テンセントテクノロジーニュース(清宇)北京時間9月6日、海外メディアの報道によると、水曜日に発表され...

VPSBlast [Banwagong] - $6.99/512m メモリ/256mvswap/10gSSD/500g トラフィック/6 データセンター

VPSBlast は IT7 のメイン VPS ブランドです。もちろん、ローエンド VPS ブランド...

FosunとソフトバンクがLicai.comに投資し、中国トップのSaaSサービスプロバイダーを共同で構築

10年以上前、ソフトバンクグループに大量の事業企画が殺到した。最終候補に挙がったアリババとタオバオは...

2012年の百度の最新アルゴリズムの分析

26日の夜、Baiduが大混乱に陥りました。皆さんもご存知のとおり、大小さまざまなウェブサイトがさま...

2024 年のクラウド トレンド: クラウド コンピューティングの将来はどうなるのでしょうか?

この記事では、2024 年のクラウド コンピューティングのトレンドについて説明します。複雑なエコシス...

ウェブサイトは断続的に利用できなくなり、DDOS攻撃を受けています

ご清聴ありがとうございます。この二日間、ウェブサイトが開けない理由を説明させてください。あるウェブサ...

取引プロセスを最適化して、ターゲットユーザーの消費意欲を高める

オンライン取引プラットフォームにとって、より重要なのは実際のコンバージョン率です。訪問者が実際に消費...

ユーザーエクスペリエンスを再定義する10のヒント

碑文:周知のとおり、IT 業界において最も重要なユーザー エクスペリエンスは、インターフェイス ユー...

vpsms: ロサンゼルス安昌 cn2 gia VPS、54 元/月、512m メモリ/1 コア/15g SSD/1T トラフィック

vpsms は、ロサンゼルスの Anchang データセンターにある cn2 gia ネットワークの...

中国でしか生まれなかった神州ペイモデルは、かつて投資家を混乱させた。

上場していなかったら、神州府は自社のビジネスモデルを外部に公表することは決してなかっただろう。文 |...

低コストで収益性の高い映画サイトを構築する方法

ご存知のとおり、映画サイトを立ち上げるには多額の費用がかかります。ローカル映画ライブラリを構築する場...

組織効率化 - ニューノーマル時代の人材戦略No.1

2022年8月10日、Mokaは「新常態下における組織効率向上-人材戦略No.1」をテーマに、北京で...

タオバオエクスプレスにノーと言う

タオバオストアを運営している友人たちは、タオバオ直通列車をよく知っていますが、これは確かに非常に効果...

物理サーバーのUSBインターフェースを仮想マシンにマッピングする方法

以前、クラスメートがH3CのCASクラウドプラットフォームを使用して仮想マシンを作成したとき、クラウ...