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 ビジネス モデルがなぜこれほど普及しているのでしょうか?

推薦する

エッジコンピューティング サービスの購入者は誰ですか?これら6つの重要な役割

企業による IoT とエッジ コンピューティングの導入には、IT 部門の関与だけでは不十分であること...

Tencent Cloudがガートナーのグローバルデータベースのマジッククアドラントに正式に選出

11月24日、国際的に権威のある調査機関であるガートナーは、「クラウドデータベース管理システムのマジ...

Docker をすぐに使い始めるのに役立つ 6 つのツール

Docker テクノロジがより多くの人々に認知されるにつれて、その適用範囲はますます広がっています。...

共同購入ウェブサイトは独立性を失いつつあり、業界大手の手足となっている

潮が引いたときに初めて、誰が裸で泳いでいるかが分かります。 「何千もの共同購入戦争」を経験した後、共...

無料ウェブホスティングの課金方法の秘密を解明

ウェブサイト構築業界に参入したばかりの初心者にとって、無料の仮想ホスティングの魅力は、何百万ドルもの...

新たな金融インフラの構築、テンセントクラウドが中国銀聯の銀聯クラウド構築を支援

5月18日、テンセントクラウドと中国銀聯は「銀聯クラウド」構築プロジェクトで正式に協力に達した。今回...

コンテンツこそが王様です。ウェブサイトのコンテンツ戦略をどのように策定すればよいでしょうか?

場合によっては、Web サイトにリンク可能なコンテンツが不足していることがあります。これを解決するに...

Webmaster.com の毎日のレポート: Suning と Redbaby が合併し、1 兆元を超える共同購入取引を達成

1. SuningがRedbabyのトップ経営陣を刷新。次のRedbabyは誰になるのか?レッドベイ...

変曲点?オラクルはより多くの顧客を獲得する必要があるかもしれない

オラクルの第4四半期決算報告によると、同社の業績は前年同期比でわずかに遅れをとったものの、非GAAP...

レノボはクラウドMSP市場に参入し、企業のクラウド移行の架け橋となる

[[231065]]企業がクラウドに移行すると、最も遠い距離が手の届く範囲になります。インターネット...

テンセントクラウドが新しい星星海サーバーをリリース、従来製品のアプリケーション規模は前年比30倍に増加

3月16日、Tencent Cloudは自社開発の新世代星星海サーバーのリリースを発表した。サーバー...

SEO外部リンク分析

現在のインターネットでは、企業が自社のウェブサイトをうまく運営したい場合、強力な SEO なしではそ...

ページコンテンツの価値を4つの側面から判断する方法

ウェブサイトが価値があるかどうかを判断することよりも、ウェブサイトの個々のページの価値を判断すること...

推奨: XenPower-6$/Xen/1g メモリ/120g ハードディスク/2T トラフィック/ダラス/ミラノ

Intel E5 v2 CPU、RAID10 のエンタープライズ ハード ディスク 12 台、データ...