導入 分散システムでは、分散ロックは最も基本的なツール クラスです。たとえば、支払い機能を備えた 2 つのマイクロサービスがデプロイされている場合、ユーザーは注文に対して 2 つの支払い操作を開始し、これらの 2 つのリクエストが 2 つのサービスに送信される可能性があります。したがって、重複送信を防ぐために分散ロックを使用する必要があります。ロックを取得したサービスは支払い操作を通常どおり実行し、ロックを取得していないサービスは重複操作を要求します。 当社では、多数の基本的なツール クラスをカプセル化しています。分散ロックを使用する場合は、次の 3 つのことだけを行う必要があります。 1. データベースにgloballocktableテーブルを作成する この記事を読んだ後、springboot-starter を使用して同じ機能を実現することもできます。しかし、私たちはこの方法では実装しませんでした。どのように実装したかを分析する別の記事を書く予定です。 この記事ではまずMySQLディストリビューションの実装を分析します テーブルを作成する CREATE TABLE `globallocktable` ( `id` int(11) NOT NULL AUTO_INCREMENT, `lockKey` varchar(60) NOT NULL COMMENT 'ロック名', `createTime` datetime NOT NULL COMMENT '作成時刻', 主キー (`id`)、 ユニークキー `lockKey` (`lockKey`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='グローバルロック'; 他の人が使用できるコンポーネント @成分 パブリッククラス GlobalLockComponent { @リソース グローバルロックテーブルDAO グローバルロックDAO; /** * ロックを取得しようとします。成功した場合は true、失敗した場合は false */ パブリックブール型tryLock(文字列キー) { GlobalLockUtil.tryLock(this.globalLockDAO, キー) を返します。 } /** * 別のプログラムがロックを占有していて、timeoutMs (ミリ秒) を超えると、ロックは強制的に解除されます。 * つまり、最初にキーに従ってレコードを削除し、次にレコードを追加します */ パブリックブール型 tryLockWithClear(文字列キー、Long timeoutMs) { GlobalLockUtil.tryLockWithClear(this.globalLockDAO、キー、timeoutMs) を返します。 } /** * ロックを解除し、キーに従ってレコードを削除します */ パブリック void releasesLock(文字列キー) { GlobalLockUtil.releasLock(this.globalLockDAO、キー); } } ロックオブジェクトは次のように定義されます パブリッククラス GlobalLockTable { プライベート整数ID; プライベート文字列 lockKey; プライベート日付createTime; // get メソッドと set メソッドを省略} GlobalLockTableDAOは以下のように定義されます。public interface GlobalLockTableDAO { int deleteByPrimaryKey(整数ID); int deleteByLockKey(文字列 lockKey); GlobalLockTable selectByLockKey(文字列キー); int insertSelectiveWithTest(GlobalLockTable レコード); } 特定のロックおよびロック解除ロジック パブリッククラス GlobalLockUtil { プライベート静的 Logger ロガー = LoggerFactory.getLogger(GlobalLockUtil.class); プライベート静的GlobalLockTable tryLockInternal(GlobalLockTableDAO lockDAO, 文字列キー) { GlobalLockTable を挿入 = 新しい GlobalLockTable(); 新しい日付を挿入します。 insert.setLockKey(キー); // 注1 int count = lockDAO.insertSelectiveWithTest(挿入); カウント == 0 の場合 GlobalLockTable 準備完了 = lockDAO.selectByLockKey(キー); logger.warn("キーをロックできません: {}, {}, {}", insert.getLockKey(), ready.getCreateTime(), 準備完了。 返却準備完了。 } logger.info("はい、キーによってロックを取得しました: {}", insert.getId(), insert.getLockKey()); null を返します。 } /** ロックを解除して再ロックするまでのタイムアウト**/ パブリック静的ブール型 tryLockWithClear(GlobalLockTableDAO lockDAO、文字列キー、Long timeoutMs) { GlobalLockTable ロック = tryLockInternal(lockDAO, キー); lock == null の場合は true を返します。 System.currentTimeMillis() - lock.getCreateTime().getTime() <= timeoutMs の場合 { logger.warn("申し訳ありませんが、キーを取得できません。: {}, {}, {}", key, lock.getId(), lock.getCreateTime()); false を返します。 } logger.warn("キーは既にタイムアウトしています: {}, {}, はクリアされます", key, timeoutMs); // 注2 int count = lockDAO.deleteByPrimaryKey(lock.getId()); カウント == 0 の場合 logger.warn("申し訳ありませんが、キーは既に他のキーによってプリエンプトされています: {}、{}", lock.getId()、lock.getLockKey()); false を返します。 } ロック = tryLockInternal(lockDAO、キー); ロック!= null ? false : true を返します。 } /** ロック **/ パブリック静的ブール型tryLock(GlobalLockTableDAO lockDAO, 文字列キー) { tryLockInternal(lockDAO, key) == null を返します。true の場合、false になります。 } /** ロック解除 **/ パブリック静的void releasesLock(GlobalLockTableDAO lockDAO、文字列キー) { lockDAO.deleteByLockKey(キー); } } このツールクラスには特に興味深い点が2つあります。まず、ポイント2(上のコードでマークされている)を見てみましょう。 1. ロックが長時間解除されないことを避けるために、Redis で実装する場合は、ロックタイムアウトを設定することができ、タイムアウト後にロックは自動的に解除されます (Redis で分散ロックを実装する方法については後で書きます)。MySQL で実装する場合は、最初に削除してから追加することができます。削除するときに、名前ではなく ID を使用して削除されることがわかります。なぜ?まず考えてみましょう 名前で削除すると、他の誰かがロックを削除し、タイムアウト期間前に名前でロックを追加したが、あなたが名前で削除した可能性があるからです。 ID で削除した場合、返される ID が 0 であれば、他の誰かが再度ロックしたため、再度取得する必要があることを意味します。 2. GlobalLockTable オブジェクト dao レイヤーの他のメソッドは説明不要です。このメソッドを見てみましょう。つまり、コードの注1 insertSelectiveWithTest の機能は、lockKey が存在する場合に挿入操作を実行せず、0 を返すことです。 lockKeyが存在しない場合は、挿入操作を実行し、1を返します。 <挿入 id="insertSelectiveWithTest" useGeneratedKeys="true" keyProperty="id" パラメータタイプ="com.javashitang.middleware.lock.mysql.pojo.GlobalLockTable"> `globallocktable` (`id`, に挿入 `lockKey`、`createTime` ) #{id,jdbcType=INTEGER}、#{lockKey,jdbcType=VARCHAR}、#{createTime,jdbcType=TIMESTAMP} を選択します 存在しない二重から (lockKey = #{lockKey,jdbcType=VARCHAR} の globallocktable から 1 つを選択) </挿入> 使用 使用したいときにはビジネスロジックを記述するだけでよいので、非常に便利です。 if (!globalLockComponent.tryLock(name)) { // ロックが取得されていない場合は戻ります。 } 試す { // ここでビジネスロジックを記述します} catch (Exception e) { ついに globalLockComponent.releasLock(名前) 要約する 以上が、MySQL を使用して分散ロックを実装するための編集者による紹介です。皆様のお役に立てれば幸いです。 以下もご興味があるかもしれません:
|
<<: JavaScript の知識: コンストラクタも関数である
目次概要演算子の改良と正確なinstanceofよりスマートなオブジェクトリテラル推論固有のシンボル...
スワップ スペースは、オペレーティング システムに関係なく、今日のコンピューティングの一般的な側面で...
HTML 初心者向けのベストプラクティスを 30 個紹介します。 1. タグを閉じたままにする過去に...
まず: <abbr> または <acronym>これら 2 つの記号は同じ意...
Vue $http get および post リクエストのクロスドメイン問題まずconfig/ind...
変更後: innodb_buffer_pool_size=576M ->256M InnoDB...
Linuxシステムでは、dockerを新しくインストールし、次のようなコマンドを入力します。dock...
先週、先生が私に数字当てゲームをするちょっとした宿題を出しました。とても面白いと思ったので、適当に書...
translate と transition は非常に強力で、習得するのは不可能だといつも感じていま...
最近、仕事で問題に遭遇しました。グローバル変数 red_heart があります。これは多くの場所で使...
たくさんのリンクおそらく、このようなサイトをたくさん見たことがあるでしょう。ページの下部に 50 個...
トランザクションは、論理的な操作のグループです。この操作グループを構成する各ユニットは、成功するか失...
背景アジャイル モデルは広く使用されており、テストは特に重要です。新しいバージョンは頻繁にリリースす...
目次【機能背景】 [生のSQL] 【独自SQL解析】 【分析手順】 [最適化されたSQL] 【最適化...
MySQL の漢字ソートの詳細な説明デフォルトでは、MySQL は日付、時刻、および英語の文字列の並...