Redis 分散ロック実装の原則を学ぶ

Redis分散ロックの実装原理

1. 前に書く

最近、インタビューでは分散システムについて話すことがよくあります。通常、面接官はサービス フレームワーク (Spring Cloud、Dubbo) から始めて、分散トランザクション、分散ロック、ZooKeeper およびその他の知識について話します。

そこでこの記事では、分散ロックの知識について説明し、特に Redis 分散ロックの実装原理を見ていきます。

正直なところ、企業の運用環境で分散ロックを使用する場合は、Redis 分散ロックなどのオープンソース ライブラリを使用することになりますが、一般的には、非常にシンプルで使いやすい Redisson フレームワークを使用するだけで済みます。

ご興味がございましたら、Redisson の公式 Web サイトをチェックして、Redisson の依存関係をプロジェクトに導入し、Redis に基づいて分散ロック ロックとロック解放を実装する方法を確認してください。

直感的に感じられるように、簡単な使用法のコード スニペットを示します。

上記のコードはどうでしょうか? 非常に簡単だと思いませんか?

さらに、Redis シングル インスタンス、Redis Sentinel、Redis クラスター、Redis マスター/スレーブなどのさまざまなデプロイメント アーキテクチャもサポートしており、これらを完全に実装できます。

2. Redisson による Redis 分散ロック実装の基本原理

さて、オープン ソース フレームワーク Redisson が手描きの描画を通じて Redis 分散ロックを実装する方法を説明しましょう。

(1) ロック機構

上の図を見てみましょう。今度はクライアントをロックする必要があります。クライアントが Redis クラスターに直面している場合、クライアントは最初にハッシュ ノードに基づいてマシンを選択します。

ここで注意してください。マシンを選択するだけです。これは重要です!

直後に、redis に Lua スクリプトが送信されます。Lua スクリプトは次のとおりです。

なぜ lua スクリプトを使用するのでしょうか?

大量の複雑なビジネス ロジックを Lua スクリプトにカプセル化し、redis に送信して、この複雑なビジネス ロジックの実行のアトミック性を確保できるためです。

では、この Lua スクリプトは何を意味するのでしょうか?

KEYS[1] は、ロックしたキーを表します。例:

RLock ロック = redisson.getLock("myLock");

ここでロックするために設定するロックキーは「myLock」です。

ARGV[1] は、ロック キーのデフォルトの生存時間を表し、デフォルトは 30 秒です。

ARGV[2] は、次のように、ロックされたクライアントの ID を表します。

8743c9c0-0795-4907-87fd-6c719a6b4586:1

説明しておくと、最初のif判定文は「exists myLock」コマンドを使って判定するもので、ロックしたいロックキーが存在しない場合はロックを行います。

どうやってロックするのですか?非常に簡単です。次のコマンドを使用します。

hset myLock

8743c9c0-0795-4907-87fd-6c719a6b4586:1 1

このコマンドを使用してハッシュ データ構造を設定します。このコマンド行を実行すると、次のようなデータ構造が表示されます。

上記は、クライアント「8743c9c0-0795-4907-87fd-6c719a6b4586:1」がロックキー「myLock」のロックを完了したことを意味します。

次に、「pexpire myLock 30000」コマンドが実行され、myLock キーの生存時間が 30 秒に設定されます。

はい、以上です、はい、ロックが完了しました。

(2) ロック相互排他機構

このとき、クライアント 2 がロックしようとして同じ Lua スクリプトを実行するとどうなるでしょうか?

とても簡単で、最初のif判定で「exists myLock」が実行され、ロックキーmyLockがすでに存在していることがわかります。

次に、2 番目の if 判定で、myLock キーのハッシュ データ構造にクライアント 2 の ID が含まれているかどうかが判断されますが、クライアント 1 の ID が含まれているため、明らかにそうではありません。

したがって、クライアント 2 は、pttl myLock によって返される数値を取得します。この数値は、myLock キーの残りの生存時間を表します。たとえば、生存時間は 15,000 ミリ秒残っています。

このとき、クライアント 2 は while ループに入り、常にロックを試みます。

(3) ウォッチドッグ自動延長機構

クライアント 1 によってロックされているロック キーのデフォルトの有効期間は 30 秒のみです。30 秒を超えた場合でも、クライアント 1 はロックを保持したいと考えます。どうすればよいですか?

単純!クライアント 1 が正常にロックされている限り、ウォッチドッグが開始されます。これはバックグラウンド スレッドであり、10 秒ごとにチェックされます。クライアント 1 がまだロック キーを保持している場合、ロックは継続的に延長されます。キーの生存時間。

(4) 再入可能なロック機構

それでは、クライアント 1 がすでにロックを保持している場合、ロックが再入可能である場合はどうなるでしょうか?

たとえば、次のコード:

今回は上記のLuaスクリプトを解析してみましょう。

最初の判断が明らかに真ではない場合、「exists myLock」はロック キーがすでに存在することを示します。

myLockのハッシュデータ構造に含まれるIDがクライアント1のID「8743c9c0-0795-4907-87fd-6c719a6b4586:1」であるため、2回目のif判定が成立します。

この時点で、再入可能ロックのロジックが実行され、次のものを使用します。

incrby myLock

8743c9c0-0795-4907-87fd-6c71a6b4586:1 1

このコマンドにより、クライアント 1 のロック数が 1 ずつ累積されます。

このとき、myLock のデータ構造は次のようになります。

ご覧のとおり、myLock のハッシュ データ構造内のクライアント ID はロックの数に対応します。

(5) リリースロック機構

lock.unlock()を実行すると分散ロックを解除できますが、このときのビジネスロジックも非常にシンプルです。

実際、率直に言うと、myLock データ構造内のロックの数は毎回 1 ずつ減ります。

ロックの数が 0 であることが判明した場合、それはクライアントがロックを保持していないことを意味し、この時点では次のものが使用されます。

「del myLock」コマンドは、このキーを Redis から削除します。

次に、別のクライアント 2 がロックの完了を試みることができます。

これは、分散ロックと呼ばれるオープンソースの Redisson フレームワークの実装メカニズムです。

一般に、運用システムでは、Redisson フレームワークによって提供されるこのクラス ライブラリを使用して、redis に基づいて分散ロックをロックおよび解放できます。

(6) 上記Redis分散ロックのデメリット

実際、上記の解決策の最大の問題は、myLock などのロック キーの値を特定の Redis マスター インスタンスに書き込むと、その値が対応するマスター スレーブ インスタンスに非同期的にコピーされてしまうことです。

ただし、このプロセス中に Redis マスターがダウンすると、マスターとバックアップが切り替わり、Redis スレーブが Redis マスターになります。

次に、クライアント 2 がロックしようとすると、新しい Redis マスター上でロックが完了し、クライアント 1 もロックに成功したと認識します。

これにより、複数のクライアントが分散ロックのロックを完了します。

現時点では、システムにはビジネス セマンティクスに間違いなく問題が発生し、その結果、さまざまなダーティ データが生成されます。

したがって、これが Redis クラスター、または Redis マスター/スレーブ アーキテクチャのマスター/スレーブ非同期レプリケーションによって引き起こされる Redis 分散ロックの最大の欠陥です。Redis マスター インスタンスがダウンすると、複数のクライアントが同時にロックを完了する可能性があります。

この記事の特典として、無料の C/C++ 開発学習教材パッケージ、技術ビデオ/コード、主要メーカーからの 1,000 件のインタビュー質問 (C++ の基礎、ネットワーク プログラミング、データベース、ミドルウェア、バックエンド開発/オーディオなど) を受け取ることができます。およびビデオ開発/Qt 開発/ゲーム開発/Linuxn カーネルなどの高度な学習教材と最適な学習ルート) ↓↓↓↓以下を参照↓↓記事の下部をクリックして無料で入手してください↓↓

おすすめ

転載: blog.csdn.net/m0_60259116/article/details/135064254