現在、ほぼすべての大規模な Web サイトとアプリケーションは分散方式で展開されています。分散シナリオにおけるデータの一貫性の問題は常に重要なトピックとなっています。分散 CAP 理論によれば、「分散システムは一貫性、可用性、およびパーティション耐性を同時に満たすことはできず、同時に満たすことができるのはそのうちの 2 つだけです」。したがって、多くのシステムでは、設計の初期段階でこれら 3 つの間でトレードオフを行う必要があります。インターネット分野のほとんどのシナリオでは、システムの高可用性と引き換えに、強力な一貫性を犠牲にする必要があります。多くの場合、最終的な時間がユーザーにとって許容可能な範囲内である限り、システムでは「最終的な一貫性」のみを確保する必要があります。 多くのシナリオでは、データの最終的な一貫性を保証するために、分散トランザクションや分散ロックなど、それをサポートする多くの技術的ソリューションが必要です。場合によっては、メソッドが同じスレッドによってのみ同時に実行されるようにする必要があります。スタンドアロン環境では、Java は実際に多くの並行処理関連の API を提供しますが、これらの API は分散シナリオでは無力です。つまり、純粋な Java API では分散ロック機能を提供できません。したがって、現在、分散ロックの実装には複数のソリューションが存在します。 分散ロックの実装には、現在、次のソリューションが一般的に使用されています。 データベースに基づいて分散ロックを実装する キャッシュ (redis、memcached、tair) に基づいて分散ロックを実装する これらの実装ソリューションを分析する前に、どのような分散ロックが必要かを考えてみましょう。 (ここではメソッドロックを例に挙げていますが、リソースロックについても同様です) 分散アプリケーション クラスターでは、同じメソッドは 1 台のマシン上の 1 つのスレッドによってのみ同時に実行されることが保証されます。 データベースに基づく分散ロックの実装 データベーステーブルに基づく 分散ロックを実装するには、ロック テーブルを直接作成し、テーブル内のデータを操作して実装するのが最も簡単な方法です。 次のようなデータベース テーブルを作成します。 CREATE TABLE `methodLock` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主キー', `method_name` varchar(64) NOT NULL DEFAULT '' COMMENT 'ロックされたメソッド名', `desc` varchar(1024) NOT NULL DEFAULT '備考', `update_time` タイムスタンプ NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'データ時刻を保存、自動生成', 主キー (`id`)、 BTREE を使用したユニーク キー `uidx_method_name` (`method_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='ロック方法'; メソッドをロックしたい場合は、次の SQL を実行します。 methodLock(method_name,desc) に値 ('method_name','desc') を挿入します method_name に一意制約を設定したので、複数のリクエストが同時にデータベースに送信された場合、データベースは 1 つの操作のみが成功することを保証します。その後、正常に操作されたスレッドはメソッドのロックを取得し、メソッド本体の内容を実行できると想定できます。 メソッドが実行されたときにロックを解除したい場合は、次の SQL を実行する必要があります。 method_name = 'method_name' の場合、methodLock から削除します。 上記の単純な実装には、次の問題があります。 1. このロックはデータベースの可用性に大きく依存します。データベースは単一ポイントです。データベースに障害が発生すると、ビジネス システムは使用できなくなります。 もちろん、上記の問題は他の方法でも解決できます。 データベースは単一のポイントですか? 2 つのデータベースを作成し、双方向でデータを同期します。障害が発生したら、すぐにバックアップ データベースに切り替えます。 データベース排他ロックに基づく データ テーブル内のレコードの追加と削除に加えて、データに組み込まれたロックを使用して分散ロックを実装することもできます。 先ほど作成したデータベース テーブルも使用します。分散ロックは、データベースの排他ロックを通じて実装できます。 MySQL InnoDB エンジンに基づいて、次の方法を使用してロック操作を実装できます。 パブリックブールロック(){ 接続.setAutoCommit(false) while(true){ 試す{ result = select * from methodLock where method_name=xxx for update; if(結果==null){ true を返します。 } } catch(例外 e){ } スリープ(1000); } false を返します。 } クエリ ステートメントの後に for update を追加すると、データベースはクエリ プロセス中にデータベース テーブルに排他ロックを追加します (ここで言及しておきたいのは、InnoDB エンジンがロックする場合、インデックスを検索するときにのみ行レベル ロックを使用し、それ以外の場合はテーブル レベル ロックを使用することです。ここでは行レベル ロックを使用するため、method_name にインデックスを追加する必要があります。このインデックスは一意のインデックスとして作成する必要があることに注意してください。そうしないと、複数のオーバーロードされたメソッドに同時にアクセスできないという問題が発生します。オーバーロードされたメソッドの場合は、パラメーター タイプも追加することをお勧めします)。レコードに排他ロックが追加されると、他のスレッドはレコードの行に排他ロックを追加できなくなります。 排他ロックを取得したスレッドは、分散ロックを取得できると想定できます。ロックを取得した後、メソッドのビジネス ロジックを実行できます。メソッドを実行した後、次のメソッドでロックを解除できます。 パブリックvoidロック解除(){ 接続をコミットします。 } connection.commit() 操作を通じてロックを解除します。 この方法は、前述のロックを解除できない、ロックがブロックされるといった問題を効果的に解決できます。 ブロックロックですか? for update ステートメントは、実行が成功するとすぐに戻り、実行が失敗した場合は成功するまでブロックされたままになります。 ただし、データベースのシングルポイントおよび再入可能性の問題を直接解決することはできません。 method_name に一意のインデックスを使用し、行レベルのロックを使用するために更新に明示的に使用していますが、ここでは別の問題がある可能性があります。ただし、MySql はクエリを最適化します。条件でインデックス フィールドが使用されている場合でも、データを取得するためにインデックスを使用するかどうかは、さまざまな実行プランのコストを判断することによって MySQL によって決定されます。MySQL は、非常に小さなテーブルなど、完全なテーブル スキャンの方が効率的であると判断した場合、インデックスを使用しません。この場合、InnoDB は行ロックではなくテーブル ロックを使用します。もしそんなことが起こったら悲劇だ。 。 。 もう 1 つの問題は、排他ロックを使用して分散ロックをロックする場合、排他ロックが長時間送信されないと、データベース接続が占有されてしまうことです。類似の接続が多すぎると、データベース接続プールが破裂する可能性があります。 要約する データベースを使用して分散ロックを実装する方法をまとめると、どちらの方法もデータベース内のテーブルに依存します。1 つはテーブル内のレコードの存在に基づいて現在ロックが存在するかどうかを判断する方法であり、もう 1 つはデータベース内の排他ロックを通じて分散ロックを実装する方法です。 データベースにおける分散ロックの利点 データベースに直接依存するので、理解しやすいです。 データベースにおける分散ロックの欠点 さまざまな問題が発生し、問題を解決する過程で、計画全体がますます複雑になります。 キャッシュに基づく分散ロックの実装 データベースに基づいて分散ロックを実装するソリューションと比較すると、キャッシュに基づくソリューションはパフォーマンスの点で優れています。さらに、多数のキャッシュをクラスターに展開して、単一ポイントの問題を解決することもできます。 当社には、Redis、memcached、Tair など、成熟したキャッシュ製品が数多くあります。 ここでは、Tair を例に、キャッシュを使用して分散ロックを実装するソリューションを分析します。インターネット上には Redis と memcached に関する記事が多数あり、直接使用できる成熟したフレームワークやアルゴリズムもいくつかあります。 Tair に基づく分散ロックの実装は実際には Redis に似ており、主な実装方法は TairManager.put メソッドを使用することです。 パブリックブール型trylock(文字列キー) { ResultCode code = ldbTairManager.put(NAMESPACE, key, "これはロックです。", 2, 0); (ResultCode.SUCCESS.equals(コード)) の場合 true を返します。 それ以外 false を返します。 } パブリックブールロック解除(文字列キー) { ldbTairManager.invalid(NAMESPACE、キー); } 上記の実装にはいくつかの問題もあります。 1. このロックには有効期限がありません。ロック解除操作が失敗すると、ロック レコードは Tair に残り、他のスレッドはロックを取得できなくなります。 もちろん、この問題を解決する方法もあります。 有効期限はありませんか? Tair の put メソッドは有効期限の渡しをサポートしており、有効期限が切れるとデータは自動的に削除されます。 しかし、有効期限はどのくらいに設定すればよいのでしょうか?有効期限が短すぎると、メソッドが実行される前にロックが自動的に解除され、同時実行の問題が発生します。時間を長く設定しすぎると、ロックを取得した他のスレッドがより長い時間待機しなければならない可能性があります。この問題は、データベースを使用して分散ロックを実装する場合にも発生します。 要約する キャッシュはデータベースの代わりに使用して分散ロックを実装することができ、パフォーマンスを向上させることができます。同時に、単一ポイントの問題を回避するために、多くのキャッシュ サービスがクラスターに展開されます。さらに、多くのキャッシュ サービスでは、Tair の put メソッドや redis の setnx メソッドなど、分散ロックを実装するために使用できるメソッドが提供されています。さらに、これらのキャッシュ サービスは、期限切れのデータの自動削除もサポートしており、タイムアウト時間を直接設定してロックの解除を制御することもできます。 キャッシュを使用して分散ロックを実装する利点 パフォーマンスが良好で実装も簡単です。 キャッシュを使用して分散ロックを実装することの欠点 ロックの有効期限をタイムアウトで制御するのはあまり信頼性が高くありません。 Zookeeper に基づく分散ロックの実装 分散ロックは、Zookeeper の一時的に順序付けられたノードに基づいて実装できます。 一般的な考え方は、各クライアントがメソッドをロックすると、ZooKeeper 上のメソッドに対応する指定されたノードのディレクトリに、一意の瞬間順序付きノードが生成されます。 ロックを取得するかどうかの判定方法は非常に簡単で、順序付けられたノードの中でシリアル番号が最も小さいものを決定するだけです。 ロックが解除されたら、一時ノードを削除するだけです。同時に、サービスのダウンタイムによりロックを解除できないために発生するデッドロックの問題を回避できます。 Zookeeper が上記の問題を解決できるかどうか見てみましょう。 ロックが解除できない? Zookeeper を使用すると、ロックが解除されない問題を効果的に解決できます。ロックを作成すると、クライアントは ZK に一時ノードを作成するからです。クライアントがロックを取得した後に突然ハングアップすると (セッション接続が切断されると)、一時ノードは自動的に削除されます。その後、他のクライアントは再度ロックを取得できます。 再入可能ではないですか? Zookeeper を使用すると、再入不可能の問題も効果的に解決できます。クライアントがノードを作成すると、現在のクライアントのホスト情報とスレッド情報がノードに直接書き込まれます。次にロックを取得するときは、現在の最小ノードのデータと比較するだけです。自分の情報と同じ場合は直接ロックを取得し、異なる場合は一時的にシーケンシャルノードを作成してキューに参加します。 問題は一つだけ? Zookeeper を使用すると、単一ポイントの問題を効果的に解決できます。ZK はクラスターに展開されます。クラスター内のマシンの半分以上が稼働している限り、外部にサービスを提供できます。 再入可能ロック サービスをカプセル化する Zookeeper サードパーティ ライブラリ Curator クライアントを直接使用できます。 パブリック ブール型 tryLock(long timeout, TimeUnit unit) は InterruptedException をスローします { 試す { interProcessMutex.acquire(timeout, unit) を返します。 } キャッチ (例外 e) { e.printStackTrace(); } true を返します。 } パブリックブールロック解除() { 試す { プロセス間ミューテックスを解放します。 } キャッチ (Throwable e) { ログエラー(e.getMessage(), e); ついに executorService.schedule(新しい Cleaner(クライアント、パス)、delayTimeForClean、TimeUnit.MILLISECONDS); } true を返します。 } Curator が提供する InterProcessMutex は分散ロックの実装です。 acquire メソッドはロックを取得するために使用され、release メソッドはロックを解放するために使用されます。 ZK を使用して実装された分散ロックは、この記事の冒頭で述べた分散ロックに対する期待をすべて完全に満たしているようです。しかし、そうではありません。Zookeeper によって実装された分散ロックには、実際には、パフォーマンスがキャッシュ サービスほど高くない可能性があるという欠点があります。ロックの作成と解放のプロセスごとに、ロック機能を実装するために瞬間的なノードを動的に作成および破棄する必要があるためです。 ZK では、ノードの作成と削除はリーダー サーバー経由でのみ実行でき、そのデータをすべてのフォロワー マシンで共有することはできません。 実際、Zookeeper を使用すると同時実行の問題が発生する可能性もありますが、これは一般的ではありません。次の状況を考えてみましょう。ネットワークジッターにより、クライアントと ZK クラスター間のセッション接続が切断されます。すると、ZK はクライアントがハングアップしたと判断し、一時ノードを削除します。この時点で、他のクライアントは分散ロックを取得できます。同時実行の問題が発生する可能性があります。 zk には再試行メカニズムがあるため、この問題は一般的ではありません。zk クラスターがクライアントのハートビートを検出できなくなると、再試行します。Curator クライアントは複数の再試行戦略をサポートしています。一時ノードは、複数回の再試行後に失敗した場合にのみ削除されます。 (したがって、適切な再試行戦略を選択し、ロックの粒度と同時実行性のバランスを見つけることも重要です。) 要約する Zookeeperを使用して分散ロックを実装する利点 単一点問題、非再入問題、非ブロッキング問題、ロックを解除できない問題を効果的に解決します。実装は比較的簡単です。 Zookeeperを使用して分散ロックを実装することの欠点 パフォーマンスは、キャッシュを使用して分散ロックを実装する場合ほど良くありません。 ZK の原則をある程度理解している必要があります。 3つのソリューションの比較 上記の方法はどれも完璧ではありません。 CAP と同様に、複雑さ、信頼性、パフォーマンスなどの要件を同時に満たすことは不可能です。したがって、さまざまなアプリケーション シナリオに応じて最も適したものを選択するのが最善の方法です。 理解の難しさの観点から(低い順から高い順) 実装の複雑さの観点から(低から高) パフォーマンスの観点から(高から低) 信頼性の観点から(高から低) 要約する 以上が、分散ロックの原理と 3 つの実装方法の詳細な説明に関するこの記事の内容のすべてです。興味のある方は、引き続き、Java のミューテックス ロック セマフォとマルチスレッド待機メカニズムの詳細な説明、Apache Zookeeper の使用例の詳細、いくつかの重要な MySQL 変数、およびこのサイトのその他の関連トピックを参照してください。皆様のお役に立てれば幸いです。ご質問がございましたら、いつでもメッセージを残してください。編集者がすぐに返信し、より良い読書体験とサポートを提供します。このサイトをサポートしてくださっている皆様、ありがとうございます! 以下もご興味があるかもしれません:
|
>>: React プロジェクトにおける TypeScript の使用の概要
1. 動的コンポーネント <!DOCTYPE html> <html> &l...
以下のように表示されます。 SELECT prod_name,prod_price FROM pro...
目次1. copy_{to,from}_user() とは何か1. copy_{to,from}_u...
ElementUIはテーブルリストのページング効果のチュートリアルを実装しています。参考までに。具体...
目次TOKEN タイマーリフレッシュ2. access_tokenの内部設計2.1 access_t...
MySQL はオープンソースの小規模リレーショナル データベース管理システムです。現在、MySQL...
目次序文1. シングルユーザーモードでの一般的なバグ修正2. シングルユーザーモードでシステムパスワ...
目次行と列の変換トランスクリプトの構成を分析するvue3 + el-table で作成されたトランス...
この記事では、パーセンテージスコアリングプログレスバーを実現するためのjQueryの具体的なコードを...
今日は、ローカルの Docker プロジェクト イメージを dockerhub に公開する方法を紹介...
目次ステップ1: インストールステップ2: 引用ステップ3: 使用Webプロジェクトでは、データを読...
mysql5.6 のグリーン バージョンを解凍すると、my-default.ini ファイルが作成さ...
この記事では、携帯電話のカメラとアルバムにアクセスするためのVueの具体的なコードを参考までに共有し...
序文全文インデックスを使用できるのは Innodb と MyISAM ストレージ エンジンのみです ...
1. 概要Docker イメージを作成するには、次の 3 つの方法があります。 Docker コミッ...