この記事は機械翻訳のミラー記事です。元の記事にジャンプするにはこちらをクリックしてください。

眺める: 10939|答える: 1

分散ロックのいくつかの使い方(redis、zookeeper、database)

[リンクをコピー]
掲載地 2018/08/30 15:04:32 | | | |
Q: サービスサーバー1台、データベース1台、操作:ユーザーの現在の残高を照会し、現在の残高の3%を手数料として差し引く

同期

DBロック

Q: サービスサーバー2台、データベース1台、操作:ユーザーの現在の残高を照会し、その3%を手数料として差し引く
分散ロック

どのような分散ロックが必要ですか?
分散アプリケーションクラスタにおいて、同じメソッドを1台のマシンで1つのスレッドで同時に実行できるようにすることを保証します。

このロックがリエントラントロックの場合(デッドロックを避ける)

このロックはブロッキングロックとして最適です(ビジネスニーズに応じて検討してください)

この錠は公平な錠前であるのが最善です(ビジネスのニーズに応じて検討してください)

取得およびリリースロック機能が非常に豊富に利用可能です

取得ロックとリリースロックの性能は優れています

1. データベースに基づく分散ロック

テーブルベースの実装に基づく分散ロック

メソッドをロックしたい場合、以下のSQLを実行します。
methodLock(method_name,desc)に値('method_name', 'desc')を挿入します。
method_nameに一意性制約を設けているため、複数のリクエストが同時にデータベースに提出された場合、データベースは1つの操作のみを成功させることを保証します。そうなれば、メソッドロックを取得し、メソッドの本文内容を実行できるスレッドができると仮定できます。

メソッドが実行された際、ロックを解除したい場合は以下のSQLを実行する必要があります:
methodLockから削除(method_name ='method_name'

上記のこの単純な実装には以下の問題があります:

  • このロックはデータベースの可用性に依存しており、データベースが停止するとビジネスシステムは利用不能になります。
  • このロックには有効期限がなく、アンロック操作が失敗するとロックレコードはデータベースに残り、他のスレッドはロックを取得できなくなります。
  • このロックはノンブロッキングのみであり、挿入が失敗するとデータの挿入操作が直接エラーを報告します。 ロックを取得しないスレッドはキューに入らず、再度ロックを取得するために再度ロック取得操作をトリガーする必要があります。
  • ロックは再入しず、同じスレッドは解除されるまで再びロックを取得することはできません。 なぜなら、そのデータにはすでに存在するデータがあるからです。
  • この錠は不公平な錠前であり、錠前を待つすべての糸が運で鍵を奪い合います。


もちろん、上記の問題は他の方法で解決することも可能です。

  • データベースは単一点なのでしょうか? 2つのデータベースを構築すると、データは双方向で同期されます。 切ったらすぐにバックアップライブラリに切り替えてください。
  • 有効期限がないのですか? 定期的にタイムアウトデータを整理するスケジュールタスクを行えばいいです。
  • 非ブロッキング? 挿入が成功するまでしばらくループを繰り返し、成功を返します。
  • ノンリエントラント? データベーステーブルにフィールドを追加して、現在ロックがかかっているマシンのホスト情報とスレッド情報を記録します。次にロックがかかったときは、まずデータベースからクエリをしてみてください。もし現在のマシンのホスト情報やスレッド情報がデータベースに見つかれば、直接そのマシンにロックを割り当てることができます。
  • 不公平? 別の中間テーブルを作成してロック待ちのすべてのスレッドを記録し、作成時間に従ってソートします。最初に作成されたスレッドのみがロックを取得することが許されます


排他的ロックに基づく分散ロック

データテーブル内のレコードの追加・削除に加え、データに付属するロックの助けを借りて分散ロックも実装可能です。

また、作成したデータベーステーブルも使っています。 分散ロックはデータベース上の排他的ロックを通じて実装できます。 MySQLベースのInnoDBエンジンは、ロックアップ操作を実装するために以下の手法を使用できます。

クエリ文の後に update を追加すると、クエリ処理中にデータベーステーブルに排他的ロックが追加されます。 レコードに排他的ロックが追加されると、他のスレッドはその行のレコードに排他的ロックを追加できなくなります。

排他的なロックを取得したスレッドは分散ロックを取得でき、ロックが得られた後、メソッドのビジネスロジックが実行され、以下の方法でアンロックされると考えられます。

Public Void Unlock(){ Connection.commit(); }

Connection.commit(経由); 錠を解除するための操作。

この方法は、前述のロックを解除できず、ロックを塞ぐことができない問題を効果的に解決できます。

ロックを塞ぐ? for update文は実行成功直後に戻り、成功するまでブロックされたままです。

ロック後にサービスがダウンして、解除できないのですか? このようにして、サービスがダウンした後もデータベースは自動的にロックを解除します。

しかし、それでもデータベースの単一ポイント、再登録、公正なロックの問題を直接解決するわけではありません。

分散ロックを実装するためにデータベースを利用する方法をまとめると、どちらもデータベース内のテーブルに依存しています。1つはテーブル内のレコードの存在からロックの有無を判断し、もう1つはデータベースの排他的ロックを通じて分散ロックを実装することです。

データベースにおける分散ロックの利点

データベースの助けを借りれば、理解しやすいです。

データベースにおける分散ロックの実装の欠点

さまざまな問題が出てきて、解決の過程で解決策はどんどん複雑になっていきます。

データベースの運用には一定のオーバーヘッドが必要であり、パフォーマンスの問題も考慮する必要があります。

2. キャッシュに基づく分散ロック

データベースベースの分散ロックソリューションと比べて、キャッシュベースの実装はパフォーマンス面でより良い性能を発揮します。

現在、Redisやmemcachedなど、成熟したキャッシュ製品がたくさんあります。 ここでは、分散ロックを実装するためにキャッシュを使うスキームを分析するために、Redisを例に挙げます。

Redisに基づく分散ロックの実装に関する関連記事はインターネット上に多くあり、主な実装方法はJedis.setNXメソッドの使用です。

上記の実装にはいくつかの問題もあります:

1. 単一点問題。

2. このロックには有効期限がなく、解除操作が失敗するとロックレコードが常にredis状態になり、他のスレッドはロックを取得できなくなります。

3. このロックは非ブロッキングのみであり、成功・失敗に関わらず直接戻ってきます。

4. このロックはリエントラントではなく、スレッドがロックを取得した後は、使われているキーがすでにRedisに存在するため、解除前に再度取得することはできません。 setNX操作はもはや実行できません。

5. このロックは不公平で、待機中のすべてのスレッドが同時にsetNX操作を開始し、運の良いスレッドがロックを得られることもあります。

もちろん、解決する方法もあります。

  • 現在では、主流のキャッシュサービスがクラスタ展開をサポートし、クラスタリングを通じて単一ポイント問題を解決しています。
  • 有効期限がないのですか? redisのsetExpireメソッドは受信期限をサポートしており、その時間が経過すると自動的にデータが削除されます。
  • 非ブロッキング? 繰り返し処刑されながらも。
  • 再入場は可能ではないのでしょうか? スレッドがロックを取得したら、現在のホスト情報とスレッド情報を保存し、次回取得する前に自分が現在のロックの所有者かどうかを確認してください。
  • 不公平? スレッドがロックを取得する前に、すべての待機スレッドをキューに入れ、その後先着先出しでロックを取得します。


redisクラスタの同期ポリシーには時間がかかり、NXの設定に成功した後にスレッドAがロックを受ける可能性がありますが、この値がスレッドBがsetNXを実行するサーバーに更新されていないため、並行性の問題が生じます。

redisの著者であるSalvatore Sanfilippoは、単一のノードよりも安全で信頼性の高い分散ロック管理(DLM)を実装したRedlockアルゴリズムを提案しました。

Redlockアルゴリズムは、互いに独立したN個のredisノードが存在し、一般的にN=5に設定され、これらのN個のノードが物理的な独立性を維持するために異なるマシン上で動作すると仮定しています。

アルゴリズムの手順は以下の通りです。

1. クライアントは現在の時刻をミリ秒単位で取得します。
2. クライアントはN個のノードのロックを取得しようとします(各ノードは前述のキャッシュロックと同じ方法でロックを取得します)、Nノードは同じキーと値のロックを取得します。 クライアントはインターフェースアクセスのタイムアウトを設定する必要があり、インターフェースのタイムアウト時間はロックタイムアウトよりもはるかに短くする必要があります。例えば、ロックは自動的に10秒の解除時間を設定し、インターフェースのタイムアウトは約5〜50msに設定されます。 これにより、redisノードがダウンした後にできるだけ早くタイムアウトでき、通常のロック使用を減らすことができます。
3. クライアントは、ステップ1で得た時間を現在の時間で差し引いてロックを取得するのにかかる時間を計算します。クライアントがロックのノードを3つ以上取得し、ロック取得までの時間がロックのタイムアウト時間より短い場合にのみ、分散ロックを得ます。
4. クライアントがロックを取得するまでの時間は、設定されたロックタイムアウト時間からステップ3で計算されたロック取得までの時間を引いたものです。
5. クライアントがロックを取得できなかった場合、クライアントはすべてのロックを順に削除します。
Redlockアルゴリズムを用いることで、最大2ノードまでハングしても分散ロックサービスが動作することが保証されており、従来のデータベースロックやキャッシュロックと比較して利用可能性が大幅に向上します。

しかし、分散した専門家が「分散ロックのやり方」という記事を書き、Redlockの正しさに疑問を呈しました。

専門家は、分散ロックを考慮する際にはパフォーマンスと正確性の2つの側面があると述べました。

高性能な分散ロックを使用し、その正確さが不要であれば、キャッシュロックの使用で十分です。

非常に信頼性の高い分散ロックを使用している場合は、厳格な信頼性の問題を考慮する必要があります。 一方、レッドロックはその正しさを満たしていません。 なぜ駄目なのですか。 専門家はいくつかの側面を挙げています。

現在、多くのプログラミング言語はGC関数を持つ仮想マシンを使用していますが、フルGCではプログラムがGCの処理を停止し、フルGCでは時間がかかり、プログラム自体に数分の遅延が生じることもあります。記事ではHBaseの例が挙げられ、HBaseや時には数分間GCが続くため、リースがタイムアウトします。 例えば、下図ではクライアント1がロックを受け取り共有リソースを処理しようとしていますが、処理しようとするとロックが切れるまでフルGCが発生します。 このようにして、クライアント2は再びロックを取得し、共有リソースの作業を始めます。 クライアント2が処理中の場合、クライアント1は完全なGCを完了し共有リソースの処理を開始し、両クライアントが共有リソースを処理できるようにします。



専門家たちは解決策を示しました。下図のように、MVCCのようです。トークンをロックに持ち込むという概念です。操作ロックが完了するたびにトークンが追加されます。共有リソースを処理する際にトークンを持ち込む。指定されたバージョンのみが共有リソースを処理できます。



さらに専門家は、このアルゴリズムは局所時に依存しており、Redisはキーの有効期限処理時に単調クロックではなくgetTimeOfDayメソッドを使って時間を取得しているため、時間の誤差も生じているとも言いました。 例えば、あるシナリオでは、クライアント1とクライアント2の2つに5つのREDISノード(A、B、C、D、E)があります。

1. クライアント1はA、B、Cからロックを取得し、DとEからロックネットワークのタイムアウトを取得する。
2. ノードCのクロックが不正確で、ロックタイムアウトが発生します。
3. クライアント2はC、D、Eからロックを無事取得し、AとBからロックネットワークのタイムアウトを取得する。
4. このようにして、クライアント1とクライアント2の両方がロックを得ます。
専門家がレッドロックの不在について述べる2つのポイントをまとめると:

1. GCやその他のシナリオはいつでも発生し、クライアントがロックを取得し、処理タイムアウトが別のクライアントにロックを取得させる可能性があります。 専門家たちは自己増分トークンの使用方法も提案しました。
2. アルゴリズムは局所時間に依存しており、クロックが不正確で、2つのクライアントが同時にロックを発生させることがあります。
したがって、専門家の結論としては、Redlockは通常通りのネットワーク遅延、制限付きプログラム中断、制限付きクロック誤差範囲でのみ動作しますが、これら3つのシナリオの境界は確認できず、専門家はRedlockの使用を推奨していません。 高い正確性要件のシナリオには、専門家がZookeeperを推奨しており、後ほど分散ロックとしてZookeeperを活用して説明します。

Redisの著者からの回答

Redisの著者は専門家の記事を見てブログを書きました。 著者は丁寧に専門家に感謝し、その後専門家の見解に異議を唱えました。

元のRedlock仕様で分析を求めました:http://redis.io/topics/distlock.だから、ありがとう、マーティン。 しかし、私はその分析には賛同しません。


REDISの著者によるロックタイムアウト問題の解決にトークンを使う方法についての議論は、以下の5つのポイントにまとめられます。

ポイント1、分散ロックの使用は一般的に、共有リソースを制御する他の方法がなく、専門家は共有リソースの処理を保証するためにトークンを使うため、分散ロックは不要です。
ポイント2:トークン生成に関しては、異なるクライアントが取得したトークンの信頼性を確保するために、トークンを生成するサービスは依然として分散ロックを必要とし、サービスの信頼性を保つ必要があります。
ポイント3、専門家が自己増量トークンについて言っている点について、redisの著者はそれが全く不要だと考えています。各クライアントはトークンとして一意のuuidを生成し、共有リソースをuuidを持つクライアントだけが処理できる状態に設定できます。これにより、ロックを取得したクライアントがロックを解除するまで、他のクライアントが共有リソースを処理できない状態にできます。
上の図に示されているように、トークン34のクライアントが書き込み中にGCを送信し、ロックがタイムアウトした場合、別のクライアントがトークン35のロックを受け取り再び書き込みを開始し、ロック競合が発生することがあります。 したがって、トークンの順序は共有リソースと組み合わせることはできません。
ポイント5、redisの著者は、ほとんどのシナリオで分散ロックは非トランザクションシナリオの更新問題を処理するために使われていると考えています。 著者は、共有リソースを処理するためにトークンを組み合わせるのが難しい状況があり、ロックに頼ってリソースをロックし処理しなければならないと言いたいはずです。
専門家が話すもう一つの時計の問題は、Redisの著者たちも説明しています。 ロック取得にかかる時間が長すぎてデフォルトのタイムアウト時間を超える場合、クライアントはこの時点でロックを取得できず、専門家による例の提案はありません。

個人的な感情

私がまとめた最初の問題は、クライアントが分散ロックを取得した後、処理中のタイムアウト後にロックが解除される可能性があることです。 以前は、データベースロックによる2分のタイムアウトについて話す際、もしタスクが注文ロックを2分以上占有している場合、他の取引センターがこの注文ロックを取得し、2つの取引センターが同時に同じ注文を処理できるようにすることができます。 通常、タスクは数秒で処理されますが、RPCリクエストに参加によって設定されたタイムアウトが長すぎて、タスク内に複数のタイムアウトリクエストがある場合、自動アンロック時間が超過される可能性が高いです。 Javaで書く場合、中央にFull GCが存在することもあり、ロックタイムアウト後にロックが解除されるとクライアントはそれを認識できなくなります。これは非常に深刻な問題です。 これはロック自体の問題ではないと思います。上記の分散ロックがタイムアウトリリースの特徴を持っていれば、そのような問題は起こります。 ロックタイムアウト関数を使うと、クライアントは共有リソースの処理を続けるのではなく、ロックタイムアウトを設定し、それに応じてアクションを取る必要があります。 Redlockのアルゴリズムは、クライアントがロックを取得した後に占有できるロック時間を返し、クライアントはその時間を超えてタスクを停止するためにこの時間を処理しなければなりません。

第二の問題は、分散した専門家がRedlockを理解していないことです。 Redlockの重要な特徴の一つは、ロック取得までの時間がロックがタイムアウトにデフォルトした合計時間からロック取得にかかる時間から差し引いた時間であるという点です。これにより、クライアントが処理するまでの時間は、ローカル時刻に関係なく相対的な時間となります。

この観点から、Redlockの正しさは十分に保証できます。 Redlockの詳細な分析では、ノードのredisと比較してRedlockが提供する主な特徴は高い信頼性であり、これは一部のシナリオで重要な特徴です。 しかし、レッドロックは信頼性を追求するために多額の費用をかけてきたと思います。

  • まず、Redlockをより信頼性にするために5つのノードを展開する必要があります。
  • その後、5つのノードにリクエストしてロックを得ます。Futureメソッドを使うと、まず5つのノードに同時にリクエストし、その後応答結果をまとめて取得できます。これにより応答時間を短縮できますが、それでも単一ノードのREDISロックよりは時間がかかります。
  • そのため、5ノードのうち3つ以上を取得する必要があるため、ロックの競合が生じることがあり、つまり全員が1〜2つのロックを取得してしまい、その結果、誰もロックを取得できなくなります。この問題は、Redisの作者がラフトアルゴリズムの本質を借用し、ランダムなタイミングでの衝突によって競合時間を大幅に短縮できますが、この問題は特にロックを初めて取得した場合にうまく回避できず、ロック取得にかかる時間コストが増加します。
  • 5つのノードのうち2つがダウンすると、ロックの利用可能性が大幅に減少します。まず、これら2つのダウンしたノードの結果がタイムアウトするまで戻らなければならず、ノードは3つしかありません。クライアントは3つのノードすべてのロックを取得しなければロックは得られず、これもより難しくなっています。
  • ネットワークパーティションが存在する場合、クライアントがロックを取得できない状況が生じることがあります。


多くの理由を分析した結果、Redlockの問題の最も重要な点は、RedLockが書き込みの一貫性を確保するためにクライアントが必要であり、バックエンドの5ノードは完全に独立しており、すべてのクライアントがこれら5つのノードを操作しなければならない点だと思います。 5ノードの中にリーダーがいる場合、クライアントはリーダーからロックを得ている限りリーダーのデータを同期できるため、分割やタイムアウト、競合などの問題が起きません。 したがって、分散ロックの正確性を確保するためには、強い一貫性を持つ分散調整サービスを使うことで問題をより良く解決できると考えています。

再び疑問が生じますが、有効期限はどのくらい設定すべきでしょうか? 無効化時間の設定方法が短すぎて、ロックがメソッド実行前に自動的に解除されるため、並行性の問題が生じます。 時間がかかりすぎると、ロックを付与する他のスレッドは長く待たなければならないかもしれません。

この問題は、分散ロックを実装するためのデータベース利用にも存在します。

現在の主流のアプローチは、取得した各ロックに短いタイムアウト時間を設定し、タイムアウトに近づくたびにスレッドを起動してタイムアウト時間を更新することです。 ロックを解除するのと同時にこのスレッドを終了してください。 例えば、Redisの公式分散ロックコンポーネントであるredissonはこの解決策を使用しています。

分散ロックの実装にキャッシュを利用する利点
良いパフォーマンスです。

分散ロック実装にキャッシュを使用する欠点
実施は責任が重すぎて、考慮すべき要素が多すぎます。

Zookeeper実装に基づく分散ロック

Zookeeperの一時的順序付きノードに基づく分散ロック。

一般的な考え方としては、各クライアントがメソッドをロックすると、指定されたノードのディレクトリ内に、zookeeper上のメソッドに対応する一意な瞬時順序ノードが生成されるというものです。 ロックを取得できるかどうかの判断方法は簡単で、順序付けられたノードの中で最もシリアル番号が小さいものを決めればよいだけです。 ロックが解除されたら、単にインスタントノードを削除します。 同時に、解除できないサービスダウンタイムによるデッドロックの問題を回避できます。

Zookeeperが先に挙げた問題を解決できるか見てみましょう。

  • ロックが開かない? Zookeeperを使うことで、ロックが解除されない問題を効果的に解決できます。なぜなら、ロックを作成する際にクライアントがZK内に一時的なノードを作成し、クライアントがロックを取得して突然ハング(セッション接続が切れた場合)になると、その一時ノードは自動的に削除されるからです。 他のクライアントは再びロックを手に入れることができます。
  • ブロックしない錠前? ノードが変更されると、Zookeeperはクライアントに通知し、クライアントは作成したノードがすべてのノードの中で最も小さい順序数かどうかを確認できます。
  • 再入場できないの? クライアントがノードを作成すると、現在のクライアントのホスト情報とスレッド情報を直接ノードに書き込み、次にロックを取得したいときに、現在の最小ノードのデータと比較できます。 情報が自分の情報と同じであれば、直接ロックを取得でき、異なる場合は一時的な逐次ノードを作成してキューに参加させます。


再び疑問が生じます。Zookeeperはクラスターで展開する必要があると分かっていますが、Redisクラスターのようなデータ同期の問題は起きるのでしょうか?

Zookeeperは、弱い一貫性、すなわち最終的な一貫性を保証する分散コンポーネントです。

ZookeeperはQuorum Based Protocolと呼ばれるデータ同期プロトコルを採用しています。 Zookeeperクラスター内にN台のZookeeperサーバーがある場合(Nは通常奇数で、3台はデータ信頼性を満たし、高い読み書き性能、5台はデータの信頼性と読み書き性能のバランスが最も良い)、ユーザーの書き込み操作はまずN/2+1台のサーバーに同期され、その後ユーザーに返され、書き込みが成功するよう促されます。 Quorum Based Protocolに基づくデータ同期プロトコルは、Zookeeperがサポートできる強度の一貫性を決定します。

分散環境では、強固な一貫性を満たすデータストレージは基本的に存在せず、1つのノードのデータ更新時にすべてのノードが同期的に更新される必要があります。 この同期戦略はマスター・スレーブ同期レプリケーションデータベースに現れます。 しかし、この同期戦略は書き込み性能に大きな影響を与え、実際にはほとんど見られません。 ZookeeperはN/2+1ノードを同期的に書き込み、N/2ノードは同期的に更新されていないため、Zookeeperは強固な一貫性を持ちません。

ユーザーのデータ更新操作は、その後の読み取りが更新された値を必ず読み取ることを保証するものではありませんが、最終的には一貫性を示します。 一貫性を犠牲にすることは、データの一貫性を完全に無視することを意味しません。そうでなければデータは混沌としており、システムの利用可能性がどれほど高くても、分布がどれほど良くても価値はありません。 一貫性を犠牲にするというのは、リレーショナルデータベースにおいて強い一貫性がもはや必要なくなったということですが、システムが最終的に一貫性を達成できる限りのことです。

一問の質問ですか? Zookeeperを使うことで、単一ポイントの問題を効果的に解決できます。ZKはクラスターで展開され、クラスター内のマシンの半数以上が生き残っていれば、外部にサービスを提供できます。

公平性の問題? Zookeeperを使うことで、公正なロックの問題を解決できます。クライアントがZKで作成した一時ノードは秩序正しく、ロックが解除されるたびにZKが最も小さいノードにロック取得を通知できるため、公平性が保証されます。

再び疑問が生じます。Zookeeperはクラスターで展開する必要があると分かっていますが、Redisクラスターのようなデータ同期の問題は起きるのでしょうか?

Zookeeperは、弱い一貫性、すなわち最終的な一貫性を保証する分散コンポーネントです。

ZookeeperはQuorum Based Protocolと呼ばれるデータ同期プロトコルを採用しています。 Zookeeperクラスター内にN台のZookeeperサーバーがある場合(Nは通常奇数で、3台はデータ信頼性を満たし、高い読み書き性能、5台はデータの信頼性と読み書き性能のバランスが最も良い)、ユーザーの書き込み操作はまずN/2+1台のサーバーに同期され、その後ユーザーに返され、書き込みが成功するよう促されます。 Quorum Based Protocolに基づくデータ同期プロトコルは、Zookeeperがサポートできる強度の一貫性を決定します。

分散環境では、強固な一貫性を満たすデータストレージは基本的に存在せず、1つのノードのデータ更新時にすべてのノードが同期的に更新される必要があります。 この同期戦略はマスター・スレーブ同期レプリケーションデータベースに現れます。 しかし、この同期戦略は書き込み性能に大きな影響を与え、実際にはほとんど見られません。 ZookeeperはN/2+1ノードを同期的に書き込み、N/2ノードは同期的に更新されていないため、Zookeeperは強固な一貫性を持ちません。

ユーザーのデータ更新操作は、その後の読み取りが更新された値を必ず読み取ることを保証するものではありませんが、最終的には一貫性を示します。 一貫性を犠牲にすることは、データの一貫性を完全に無視することを意味しません。そうでなければデータは混沌としており、システムの利用可能性がどれほど高くても、分布がどれほど良くても価値はありません。 一貫性を犠牲にするというのは、リレーショナルデータベースにおいて強い一貫性がもはや必要なくなったということですが、システムが最終的に一貫性を達成できる限りのことです。

Zookeeperが因果的一貫性を満たすかどうかは、クライアントのプログラム方法によります。

因果的一貫性を満たさない実践

  • プロセスAはZookeeperの/zにデータを書き込み、正常に返します
  • プロセスAはプロセスBにAが/zのデータを修正したことを知らせます
  • BはZookeeperの/zデータを読み込みます
  • Bに接続されたZookeeperのサーバーがAの書き込みデータで更新されていない可能性があるため、BはAの書き込みデータを読み取ることができません


因果的一貫性を満たす実践

  • プロセスBはZookeeperの/zでのデータ変更を聞き取ります
  • プロセスAはデータをZookeeperの/zに書き込み、そのデータが正常に戻る前に、Zookeeperは/zに登録されたリスナーを呼び、リーダーがBにデータ変更を通知します
  • プロセスBのイベント応答メソッドが応答した後、変更されたデータを受け取るので、Bは確実に変更された値を取得できます
  • ここでの因果的整合性とは、リーダーとBの間の因果的整合性、すなわちリーダーがデータに変化を通知することを意味します


2つ目のイベントリスニング機構は、Zookeeperを適切にプログラムするためにも使用すべき方法であり、Zookeeperは因果的一貫性を満たす必要があります

したがって、Zookeeperに基づく分散ロックを実装する際には、因果的一貫性を満たす慣習を用いるべきです。つまり、ロックを待つスレッドはZookeeperのロックの変更を受信し、ロックが解放されると、Zookeeperは公正なロック条件を満たす待機スレッドに通知します。

Zookeeperのサードパーティライブラリクライアントを直接使うことができ、これはリエントラントロックサービスをカプセル化しています。

ZKで実装された分散ロックは、この記事冒頭で期待していた分散ロックにまさに合致しているように思えます。 しかし実際にはそうではなく、Zookeeperが実装した分散ロックには欠点があり、パフォーマンスがキャッシュサービスほど高くない場合があります。 なぜなら、ロックを作成・解放するたびに、ロック機能を実現するためには瞬時ノードを動的に作成・破壊しなければならないからです。 ZK内でノードの作成および削除はリーダーサーバーを通じてのみ行え、その後データはすべてのフォロワーマシンと共有されます。

Zookeeperを使って分散ロックを実装する利点
単一ポイント問題、再突入不可問題、非ブロッキング問題、ロック解除失敗を効果的に解決します。 実装は比較的簡単です。

Zookeeperを分散ロック実装に使う欠点
パフォーマンスは、分散ロックを実装するためにキャッシュを使うほど良くありません。 ZKの原理を理解する必要があります。

三つの選択肢の比較

理解しやすさの観点から(低いレベルから高いレベルへ)
Zookeeper> Cache > Zookeeper データベース

実装の複雑さの観点から(低から高)
Zookeeper > キャッシュ>データベース

パフォーマンスの観点から(高いものから低いものまで)
Zookeeper > > キャッシュ

信頼性の観点から(高いものから低いものへ)
Zookeeper > キャッシュ>データベース





先の:春@Beanと@Service注釈の違い
次に:C言語をゼロから学ぶ動画チュートリアル
 地主| 掲載地 2020/09/22 17:34:33 |
[実戦]。 NET CoreはRedisを基盤とした分散ロックを実装しています
https://www.itsvse.com/thread-9391-1-1.html

.net/c# Zookeeper分散ロック実装 [ソースコード]
https://www.itsvse.com/thread-4651-1-1.html

免責事項:
Code Farmer Networkが発行するすべてのソフトウェア、プログラミング資料、記事は学習および研究目的のみを目的としています。 上記の内容は商業的または違法な目的で使用されてはならず、そうでなければ利用者はすべての結果を負うことになります。 このサイトの情報はインターネットからのものであり、著作権紛争はこのサイトとは関係ありません。 ダウンロード後24時間以内に上記の内容を完全にパソコンから削除してください。 もしこのプログラムを気に入ったら、正規のソフトウェアを支持し、登録を購入し、より良い本物のサービスを受けてください。 もし侵害があれば、メールでご連絡ください。

Mail To:help@itsvse.com