分散ロックの複数の実装

分散ロックの複数の実装

現在、ほぼすべての大規模な Web サイトとアプリケーションは分散形式で展開されています。分散シナリオにおけるデータの一貫性の問題は常に重要なトピックです。分散 CAP 理論によれば、「分散システムは一貫性、可用性、およびパーティション耐性を同時に満たすことはできず、同時に満たすことができるのはそのうちの 2 つだけである」とされています。したがって、多くのシステムでは、設計の初期段階でこれら 3 つの間でトレードオフを行う必要があります。インターネット分野のほとんどのシナリオでは、システムの高可用性と引き換えに強力な一貫性を犠牲にする必要があります。多くの場合、最終的な時間がユーザーにとって許容可能な範囲内である限り、システムは「最終的な一貫性」を確保するだけで済みます。

[[224851]]

多くのシナリオでは、データの最終的な一貫性を保証するために、分散トランザクションや分散ロックなど、それをサポートする多くの技術的ソリューションが必要です。場合によっては、メソッドが同じスレッドによってのみ同時に実行されるようにする必要があります。スタンドアロン環境では、Java は実際に多くの並行処理関連の API を提供しますが、これらの API は分散シナリオでは無力です。つまり、純粋な Java API では分散ロック機能を提供できません。したがって、現在、分散ロックの実装には複数のソリューションが存在します。

分散ロックの実装には、現在、次のソリューションが一般的に使用されています。

  • データベースに基づく分散ロックの実装
  • キャッシュに基づく分散ロックの実装
  • Zookeeper に基づく分散ロックの実装

これらの実装ソリューションを分析する前に、どのような分散ロックが必要かを考えてみましょう。 (ここではメソッドロックを例に挙げていますが、リソースロックについても同様です)

分散アプリケーション クラスターでは、同じメソッドは 1 台のマシン上の 1 つのスレッドによってのみ同時に実行されることが保証されます。

  • このロックは再入可能ロックである必要があります(デッドロックを回避するため)
  • このロックはブロッキング ロックです (ビジネス ニーズに基づいてこのロックを使用するかどうかを検討してください)
  • 可用性の高いロック取得およびロック解放機能
  • ロックの取得と解放のパフォーマンスが向上しました

データベースに基づく分散ロックの実装

データベーステーブルに基づく

分散ロックを実装するには、ロック テーブルを直接作成し、テーブル内のデータを操作して実装するのが最も簡単な方法です。

メソッドまたはリソースをロックする場合は、テーブルにレコードを追加し、ロックを解除する場合はレコードを削除します。

次のようなデータベース テーブルを作成します。

  1. 作成する テーブル`methodLock` (
  2. `id` int (11)はない  NULL AUTO_INCREMENT COMMENT '主キー' ,
  3. `method_name` varchar (64) NOT   NULL  デフォルト  '' COMMENT 'ロックされたメソッド名'
  4. ` desc` varchar (1024)なし  NULL  デフォルト  「備考」
  5. `update_time`タイムスタンプ ない  NULL  デフォルト 現在のタイムスタンプ の上 アップデート  CURRENT_TIMESTAMP COMMENT 'データ時刻を保存、自動生成'
  6. 主要な キー(`id`)、
  7. 個性的 キー`uidx_method_name` (`method_name `) BTREE の使用
  8. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT= 'ロック方法' ;

メソッドをロックしたい場合は、次の SQL を実行します。

  1. 入れる  methodLock(method_name, desc )('method_name',' desc ')

method_name に一意の制約を設定したので、複数のリクエストが同時にデータベースに送信された場合、データベースは 1 つの操作のみが成功することを保証します。次に、正常に動作したスレッドはメソッドのロックを取得し、メソッド本体の内容を実行できると想定できます。

メソッドが実行されたときにロックを解除したい場合は、次の SQL を実行する必要があります。

  1. 消去  methodLockから method_name = 'method_name'  

上記の単純な実装には、次の問題があります。

1. このロックはデータベースの可用性に大きく依存します。データベースは単一のポイントです。データベースに障害が発生すると、ビジネス システムは利用できなくなります。

2. このロックには有効期限はありません。ロック解除操作が失敗すると、ロック レコードはデータベースに残り、他のスレッドはロックを取得できなくなります。

3. データの挿入操作では、挿入が失敗した場合にエラーが直接報告されるため、このロックは非ブロッキングのみになります。ロックを取得していないスレッドはキューに入りません。ロックを再度取得したい場合は、ロック取得操作を再度トリガーする必要があります。

4. このロックは再入不可能であり、ロックを解放する前に同じスレッドが再度ロックを取得することはできません。データは既にデータ内に存在しているからです。

もちろん、上記の問題は他の方法でも解決できます。

  • データベースは単一のポイントですか? 2 つのデータベースを作成し、双方向でデータを同期します。障害が発生したら、すぐにバックアップ データベースに切り替えます。
  • 有効期限はありませんか?定期的にデータベース内のタイムアウト データをクリーンアップするスケジュールされたタスクを実行するだけです。
  • 非ブロッキング?挿入が成功するまで while ループを作成し、その後成功を返します。
  • 再入不可能?現在ロックを取得しているマシンのホスト情報とスレッド情報を記録するためのフィールドをデータベース テーブルに追加します。次にロックを取得するときに、まずデータベースをクエリします。現在のマシンのホスト情報とスレッド情報がデータベースで見つかった場合は、ロックを直接割り当てることができます。

データベース排他ロックに基づく

データ テーブル内のレコードの追加と削除に加えて、データに組み込まれたロックを使用して分散ロックを実装することもできます。

先ほど作成したデータベース テーブルも使用します。分散ロックは、データベースの排他ロックを通じて実装できます。 MySQL InnoDB エンジンに基づいて、次の方法を使用してロック操作を実装できます。

  1. パブリックブールロック(){
  2. 接続.setAutoCommit( false )
  3. while( true ){
  4. 試す{
  5. 結果 = select * from methodLock where method_name=xxx for  アップデート;
  6. 結果がnull場合
  7. 戻る 真実;
  8. }
  9. }catch(例外 e){
  10.  
  11. }
  12. スリープ(1000);
  13. }
  14. 戻る 間違い;
  15. }

クエリ ステートメントの後に for update を追加すると、データベースはクエリ プロセス中にデータベース テーブルに排他ロックを追加します (ここで言及しておきたいのは、InnoDB エンジンがロックする場合、インデックスを検索するときにのみ行レベル ロックを使用し、それ以外の場合はテーブル レベル ロックを使用することです。ここでは行レベル ロックを使用するため、method_name にインデックスを追加する必要があります。このインデックスは一意のインデックスとして作成する必要があることに注意してください。そうしないと、複数のオーバーロードされたメソッドに同時にアクセスできないという問題が発生します。オーバーロードされたメソッドの場合は、パラメーター タイプも追加することをお勧めします)。レコードに排他ロックが追加されると、他のスレッドはレコードの行に排他ロックを追加できなくなります。

排他ロックを取得したスレッドは分散ロックも取得できると考えられます。ロックを取得した後、メソッドのビジネス ロジックを実行できます。メソッドを実行した後、次の方法でロックを解除できます。

  1. パブリックvoidロック解除(){
  2. 繋がり専念();
  3. }

connection.commit() 操作を通じてロックを解除します。

この方法は、前述のロックを解除できない、ロックがブロックされるといった問題を効果的に解決できます。

  • ブロックロックですか? for update ステートメントは、実行が成功するとすぐに戻り、実行が失敗した場合は成功するまでブロックされたままになります。
  • ロック後、サービスがクラッシュし、解放できなくなりますか?この方法を使用すると、サービスが停止した後、データベース自体がロックを解除します。

ただし、データベースのシングルポイントおよび再入可能性の問題を直接解決することはできません。

method_name に一意のインデックスを使用し、行レベルのロックを使用するために更新に明示的に使用していますが、ここでは別の問題がある可能性があります。ただし、MySql はクエリを最適化します。条件でインデックス フィールドが使用されている場合でも、データを取得するためにインデックスを使用するかどうかは、さまざまな実行プランのコストを判断することによって MySQL によって決定されます。 MySQL は、非常に小さなテーブルなど、テーブル全体のスキャンの方が効率的であると判断した場合、インデックスを使用しません。この場合、InnoDB は行ロックではなくテーブルロックを使用します。もしそんなことが起こったら悲劇だ。 。 。

もう 1 つの問題は、排他ロックを使用して分散ロックをロックする場合、排他ロックが長時間送信されないと、データベース接続が占有されてしまうことです。類似の接続が多すぎると、データベース接続プールが破裂する可能性があります。

要約する

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

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

データベースに直接依存するので、理解しやすいです。

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

さまざまな問題が発生し、問題を解決する過程で、計画全体がますます複雑になります。

データベースを操作するには、ある程度のオーバーヘッドが必要であり、パフォーマンスの問題を考慮する必要があります。

データベースで行レベルのロックを使用することは、特にロック テーブルが大きくない場合には、必ずしも信頼できるとは限りません。

キャッシュに基づく分散ロックの実装

データベースに基づいて分散ロックを実装するソリューションと比較すると、キャッシュに基づくソリューションはパフォーマンスの点で優れています。さらに、多数のキャッシュをクラスターに展開して、単一ポイントの問題を解決することもできます。

当社には、Redis、memcached、Tair など、成熟したキャッシュ製品が数多くあります。

ここでは、Tair を例に、キャッシュを使用して分散ロックを実装するソリューションを分析します。インターネット上には Redis と memcached に関する記事が多数あり、直接使用できる成熟したフレームワークやアルゴリズムもいくつかあります。

Tair に基づく分散ロックの実装は実際には Redis に似ており、主な実装方法は TairManager.put メソッドを使用することです。

  1. パブリックブール型trylock(文字列キー) {
  2. ResultCode code = ldbTairManager.put(NAMESPACE, key , "これはロックです。" , 2, 0);
  3. (ResultCode.SUCCESS.equals(コード)) の場合
  4. 戻る 真実;
  5. それ以外 
  6. 戻る 間違い;
  7. }
  8. パブリックブールロック解除(文字列キー) {
  9. ldbTairManager.invalid(NAMESPACE、キー);
  10. }

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

1. このロックには有効期限はありません。ロック解除操作が失敗すると、ロック レコードは Tair に残り、他のスレッドはロックを取得できなくなります。

2. このロックは非ブロッキングのみが可能で、成功か失敗かに関係なく直接戻ります。

3. このロックは再入不可能です。スレッドがロックを取得した後、使用されたキーがすでに Tair に存在するため、ロックを解放する前に再度ロックを取得することはできません。 put 操作は実行できなくなりました。

もちろん、この問題を解決する方法もあります。

  • 有効期限はありませんか? Tair の put メソッドは有効期限の渡しをサポートしており、有効期限が切れるとデータは自動的に削除されます。
  • 非ブロッキング? while は実行を繰り返します。
  • 再入不可能?スレッドはロックを取得した後、現在のホスト情報とスレッド情報を保存し、次回ロックを取得する前に現在のロックの所有者であるかどうかを確認します。

しかし、有効期限はどのくらいに設定すればよいのでしょうか?有効期限が短すぎると、メソッドが実行される前にロックが自動的に解除され、同時実行の問題が発生します。時間を長く設定しすぎると、ロックを取得した他のスレッドがより長い時間待機しなければならない可能性があります。この問題は、データベースを使用して分散ロックを実装する場合にも発生します。

要約する

データベースの代わりにキャッシュを使用すると、分散ロックを実装でき、パフォーマンスが向上します。同時に、単一ポイントの問題を回避するために、多くのキャッシュ サービスがクラスターに展開されます。さらに、多くのキャッシュ サービスでは、Tair の put メソッドや redis の setnx メソッドなど、分散ロックを実装するために使用できるメソッドが提供されています。さらに、これらのキャッシュ サービスは、期限切れのデータの自動削除もサポートしており、タイムアウト時間を直接設定してロックの解除を制御することもできます。

キャッシュを使用して分散ロックを実装する利点

パフォーマンスが良好で実装も簡単です。

キャッシュを使用して分散ロックを実装することの欠点

ロックの有効期限をタイムアウトで制御するのはあまり信頼性が高くありません。

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

分散ロックは、Zookeeper の一時的に順序付けられたノードに基づいて実装できます。

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

Zookeeper が上記の問題を解決できるかどうか見てみましょう。

  • ロックが解除できない? Zookeeper を使用すると、ロックを作成するときにクライアントが ZK に一時ノードを作成するため、ロックが解放されない問題を効果的に解決できます。ロックを取得した後にクライアントが突然ハングアップすると(セッション接続が切断されると)、一時ノードは自動的に削除されます。その後、他のクライアントは再度ロックを取得できます。
  • 非ブロッキングロック? Zookeeper はブロッキング ロックを実装するために使用できます。クライアントは、ZK 内に連続ノードを作成し、そのノードにリスナーをバインドできます。ノードが変更されると、Zookeeper はクライアントに通知します。クライアントは、作成したノードが現在のすべてのノードの中で最小のシーケンス番号を持っているかどうかを確認できます。そうであれば、ロックを取得し、ビジネス ロジックを実行できます。
  • 再入可能ではないですか? Zookeeper を使用すると、再入不可能の問題も効果的に解決できます。クライアントがノードを作成すると、現在のクライアントのホスト情報とスレッド情報がノードに直接書き込まれます。次にロックを取得するときは、現在の最小ノードのデータと比較するだけです。情報が自分のものと同じであれば、ロックを直接取得できます。異なる場合は、キューに参加するための一時的な順次ノードを作成します。
  • 問題は一つだけ? Zookeeper を使用すると、単一ポイントの問題を効果的に解決できます。 ZK はクラスターにデプロイされます。クラスター内のマシンの半分以上が稼働している限り、外部にサービスを提供できます。

再入可能ロック サービスをカプセル化する Zookeeper サードパーティ ライブラリ Curator クライアントを直接使用できます。

  1. パブリックブール型 tryLock(long timeout, TimeUnit unit) は InterruptedException をスローします {
  2. 試す {
  3. interProcessMutex.acquire(timeout, unit)を返します
  4. } キャッチ (例外 e) {
  5. e.printStackTrace();
  6. }
  7. 戻る 真実;
  8. }
  9. パブリックブールロック解除() {
  10. 試す {
  11. プロセスミューテックスを解放します。
  12. } キャッチ (Throwable e) {
  13. ログエラー(e.getMessage(), e);
  14. ついに
  15. executorService.schedule(新しい Cleaner(クライアント、パス)、delayTimeForClean、TimeUnit.MILLISECONDS);
  16. }
  17. 戻る 真実;
  18. }

Curator が提供する InterProcessMutex は分散ロックの実装です。 acquire メソッドはロックを取得するために使用され、release メソッドはロックを解放するために使用されます。

ZK を使用して実装された分散ロックは、この記事の冒頭で述べた分散ロックに対する期待をすべて完全に満たしているようです。しかし、そうではありません。 Zookeeper によって実装された分散ロックには、実際には、パフォーマンスがキャッシュ サービスほど高くない可能性があるという欠点があります。ロックの作成と解放のプロセスごとに、ロック機能を実装するために瞬間的なノードを動的に作成および破棄する必要があるためです。 ZK では、ノードの作成と削除はリーダー サーバー経由でのみ実行でき、そのデータをすべてのフォロワー マシンで共有することはできません。

実際、Zookeeper を使用すると同時実行の問題が発生する可能性もありますが、これは一般的ではありません。次の状況を考えてみましょう。ネットワーク ジッターにより、クライアントと ZK クラスター間のセッション接続が切断されます。その後、ZK はクライアントが切断したと判断し、一時ノードを削除します。このとき、他のクライアントは分散ロックを取得できます。同時実行の問題が発生する可能性があります。 zk には再試行メカニズムがあるため、この問題は一般的ではありません。 zk クラスターがクライアントのハートビートを検出できない場合は、再試行します。 Curator クライアントは複数の再試行戦略をサポートしています。一時ノードは、複数回の再試行後に失敗した場合にのみ削除されます。 (したがって、適切な再試行戦略を選択し、ロックの粒度と同時実行性のバランスを見つけることも重要です。)

要約する

Zookeeperを使用して分散ロックを実装する利点

単一点問題、非再入問題、非ブロッキング問題、ロックを解除できない問題を効果的に解決します。実装は比較的簡単です。

Zookeeperを使用して分散ロックを実装することの欠点

パフォーマンスは、キャッシュを使用して分散ロックを実装する場合ほど良くありません。 ZK の原則をある程度理解している必要があります。

3つのソリューションの比較

上記のいずれの方法でも***を達成できません。 CAP と同様に、複雑さ、信頼性、パフォーマンスなどの要件を同時に満たすことは不可能です。したがって、最善の方法は、さまざまなアプリケーション シナリオに応じて最も適切なものを選択することです。

理解の難しさの観点から(低い順から高い順)

データベース > キャッシュ > Zookeeper

実装の複雑さの観点から(低から高)

Zookeeper >= キャッシュ > データベース

パフォーマンスの観点から(高から低)

キャッシュ > Zookeeper >= データベース

信頼性の観点から(高から低)

Zookeeper > キャッシュ > データベース

[この記事は、51CTOコラムニストのホリス、著者WeChatパブリックアカウントホリス(ID:hollischuang)によるオリジナル記事です]

この著者の他の記事を読むにはここをクリックしてください

<<:  BitCloudは産業用ソフトウェアのデジタル配信を加速します

>>:  Zang Chengwei: レスポンシブアーキテクチャの実践に基づく Meituan Dianping のクライアント

推薦する

SEMメディカルネットワーク編集長

ウェブサイト編集者は、ウェブサイトの全体的なコンテンツとメインスタイルを作成します。ウェブサイトが訪...

インターネット製品のインタラクティブレビューをより効果的に実施する方法

インタラクティブプロトタイプは、製品開発プロセスにおける重要な出力です。製品のプレゼンテーション形式...

ファイルメディア-512m メモリ/10g SSD/1T トラフィック/G ポート/月額 3.99 ユーロ

filemedia は 2008 年に設立されたドイツのホスティング サービス プロバイダーです。現...

2012年山東インターネットウェブマスター会議が済南で成功裏に開催されました

11月26日のAdmin5 Webmaster Networkによると、山東インターネットウェブマス...

lfchosting-1G メモリ XEN/4 コア/25G ハードディスク/1T トラフィック/G ポート/6.5 USD/月

LFCHOSTING は 1996 年に設立されたホスティング会社です。その事業には、仮想ホスティン...

短期間で新しいウェブサイトの Baidu インデックスと重みを取得する方法

みなさんこんにちは。私は湖南省出身のキネスです。今回は、編集者が新しいウェブサイトを最適化し、短期間...

Baiduプロモーションキーワードの価格が高い場合のコスト削減方法

資金が少しある多くの企業が百度プロモーションを行っています。百度プロモーションは確かに短期間で成果を...

2019 年にクラウド IT インフラストラクチャの需要が変動し続ける理由

調査によると、エンタープライズ ハイブリッド クラウド環境に導入されるサーバー、ディスク ストレージ...

holderhost-$7/3Gメモリ/150Gハードディスク/4Tトラフィック/Gポート/フェニックスシティ

holderhost.com の openvz 用大容量メモリ VPS をご紹介します。サーバー構成...

運用・プロモーション:洗練されたオムニチャネル運用プラン!

広告に多額の費用を費やしたのに、なぜ成果が見られないのでしょうか?ユーザーは来たのになぜ買わないので...

ugvps ブラックフライデーセール $4.99 2G RAM 100M 帯域幅 無制限トラフィック

ugvps ブラックフライデーセール 月額 4.99 ドル、2G メモリ、100M サーバー帯域幅、...

GoogleとBaiduのもう一つのアイデンティティ:ドメイン名登録業者

この記事を始める前に、まず明確にしておきたいのは、Google は長い間世界トップクラスのドメイン名...

報告書:貧しい地域ではインターネットへのアクセスにモバイルデバイスを使用する傾向が高い

中国インターネット情報センターの報告によると、2014年12月現在、中国のインターネット利用者数は6...

YYを「密かに」店頭から撤去し、匿名ソーシャルアプリ「プライベートサークル」を立ち上げた

[要約] 秘密権使用者の特徴は、若く、教養が高く、活力があり、新しいものを好むことです。彼らは家にこ...

「中年リスク」を知る

最近、みんなが知乎を開く衝動は何でしょうか?ソウルジョークの作者たちは次のように答えた。知乎といえば...