導入 分散システムでは、分散ロックは最も基本的なツール クラスです。たとえば、支払い機能を備えた 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 の知識: コンストラクタも関数である
この記事では、例を使用して、MySQL でスケジュールされたタスクを設定する方法について説明します。...
MySQL 外部キー制約の無効化と有効化: MySQL 外部キー制約が有効になっているかどうかは、グ...
マスターのメソッドによると、原因は sysctl net.ipv4.ip_forward であること...
私たちウェブマスターは皆、ウェブサイトを最適化する際に記事内のキーワードを太字にすることが最適化に非...
目次1. これは2. この点を修正する1. call() メソッド2. apply() メソッド要約...
目次サーバー層でのフルテーブルスキャンの影響InnoDB におけるフルテーブルスキャンの影響Inno...
目次マスターの後ろの秒数オリジナルの実装最終マスタータイムスタンプマスターとのクロック差他の実行時間...
privot は、多対多の関係の中間テーブルです。 PT5 フレームワークは自動的に privot ...
大きなことも小さなことも考えて、方向転換しましょう。 Linux では非常に大きなファイルに遭遇する...
問題の説明最近、仕事中に問題が発生しました。MySQL が起動に失敗しました。エラー ログは次のとお...
最近、イントラネットポータルを修正していたときに、フィルターを使用する必要がある箇所に遭遇しました。...
最近 CSS を勉強していたとき、 2 つの CSS プロパティだけを使用して全画面スクロール効果を...
目次1. 反射とは何ですか? 2. JavaScriptで反映する2.1 Reflect.get(タ...
序文apt-get コマンドは、Ubuntu システムのパッケージ管理ツールです。パッケージのインス...
まずは緑色の無料インストール版のMySQLをダウンロードします。任意のフォルダに入れて構いません。今...