MySQL の悲観的ロックと楽観的ロックの理解と応用分析

MySQL の悲観的ロックと楽観的ロックの理解と応用分析

この記事では、例を使用して MySQL の悲観的ロックと楽観的ロックについて説明します。ご参考までに、詳細は以下の通りです。

悲観的ロックと楽観的ロックは、人間が定義した概念です。並行リソースを処理するための一般的な手段である一種の考え方として理解できます。

これらを、MySQL で提供されているロック メカニズム (テーブル ロック、行ロック、排他ロック、共有ロック) と混同しないでください。

1. 悲観的ロック

名前が示すように、データ処理に対して悲観的であり、同時実行の競合が発生することを常に信じ、データを取得および変更するときに他の人がデータを変更することを意味します。したがって、データ処理プロセス全体を通じてデータをロックする必要があります。

悲観的ロックの実装は通常、MySQL の排他ロック、select .... for update などのデータベースが提供するロック メカニズムに依存して悲観的ロックを実装します。

例: フラッシュセール中は、過剰販売を避けるために在庫数量が削減されます。

テーブル `tb_goods_stock` を作成します (
 `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
 `goods_id` bigint(20) unsigned DEFAULT '0' COMMENT '商品ID',
 `nums` int(11) unsigned DEFAULT '0' COMMENT '製品在庫数',
 `create_time` datetime DEFAULT NULL COMMENT '作成時刻',
 `modify_time` datetime DEFAULT NULL COMMENT '更新時間',
 主キー (`id`)、
 ユニークキー `goods_id` (`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='製品在庫テーブル';

データベース レベルで負の数が発生しないように、製品在庫数量の数値フィールド タイプを unsigned に設定します。

悲観的ロックを使用するには、MySQL の自動コミット機能をオフにして、autocommit = 0 を設定する必要があることに注意してください。

MySQL の行レベルのロックはインデックスに基づいていることに注意してください。SQL がインデックスを使用しない場合、テーブル全体のロックにはテーブルレベルのロックが使用されます。

1. トランザクションを開始し、販売する製品を照会し、レコードをロックします。

始める;
更新のために、goods_id = {$goods_id} である tb_goods_stock から数値を選択します。

2. 商品の数量が購入数量より多いかどうかを判断します。満足できない場合は、トランザクションをロールバックします。

3. 条件が満たされた場合は、在庫を減らしてトランザクションをコミットします。

tb_goods_stock を更新し、nums = nums - {$num} を設定します。 
ここで、goods_id = {$goods_id} かつ nums >= {$num} です。
専念;

トランザクション中に保持されたロックは、トランザクションがコミットされると解除されます。

悲観的ロックは、最初にロックしてから同時実行制御でデータを処理するという保守的な戦略を採用しています。データ処理のセキュリティは確保されますが、効率も低下します。

2. 楽観的ロック

名前が示すように、これはデータ処理に対して楽観的な姿勢を取り、データが一般的に競合しないという楽観的な考えを持つことを意味します。データの更新を送信するときにのみ、データの競合が検出されます。

競合が見つかった場合は、エラー メッセージがユーザーに返され、ユーザーは続行方法を決定できます。

楽観的ロックの実装は、データベースが提供するロック メカニズムに依存せず、自分で実装する必要があります。実装方法は一般的にデータ バージョンを記録するもので、1 つはバージョン番号によるもので、もう 1 つはタイムスタンプによるものです。

テーブルにバージョン番号またはタイムスタンプフィールドを追加します。データを読み取るときは、バージョン番号も一緒に読み取ります。データが更新されると、バージョン番号が 1 増加します。

データの更新を送信するときに、現在のバージョン番号が最初に読み取られたバージョン番号と等しいかどうかを判断します。等しい場合は更新されます。等しくない場合は、データは期限切れとみなされ、更新は拒否され、ユーザーは再度操作する必要があります。

テーブル `tb_goods_stock` を作成します (
 `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
 `goods_id` bigint(20) unsigned DEFAULT '0' COMMENT '商品ID',
 `nums` int(11) unsigned DEFAULT '0' COMMENT '製品在庫数',
 `create_time` datetime DEFAULT NULL COMMENT '作成時刻',
 `modify_time` datetime DEFAULT NULL COMMENT '更新時間',
 `version` bigint(20) unsigned DEFAULT '0' COMMENT 'バージョン番号',
 主キー (`id`)、
 ユニークキー `goods_id` (`goods_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='製品在庫テーブル';

1. 販売する製品を照会し、バージョン番号を取得します。

始める;
tb_goods_stock から、goods_id = {$goods_id} である nums、version を選択します。

2. 商品の数量が購入数量より多いかどうかを判断します。満足できない場合は、トランザクションをロールバックします。

3. 条件が満たされた場合は、在庫を削減します。 (アップデートの際は、現在のバージョンが手順1で取得したバージョンと同じかどうかを確認してください)

tb_goods_stock を更新し、nums = nums - {$num}、version = version + 1 に設定します。 
ここで、goods_id = {$goods_id} 
version = {$version} かつ nums >= {$num};

4. 更新操作が正常に実行されたかどうかを判断します。成功した場合はコミットし、そうでない場合はロールバックします。

楽観的ロックはプログラムに基づいて実装されるため、デッドロックが発生せず、読み取りが多いアプリケーションのシナリオに適しています。競合が頻繁に発生すると、上位アプリケーションがユーザーに再操作を要求し続けることになり、パフォーマンスが低下します。この場合は、悲観的ロックがより適しています。

MySQL 関連のコンテンツにさらに興味がある読者は、次のトピックを確認してください: 「MySQL データベース ロック関連スキルの概要」、「MySQL ストアド プロシージャ スキルの概要」、「MySQL 共通関数の概要」、「MySQL ログ操作スキルの概要」、および「MySQL トランザクション操作スキルの概要」。

この記事が皆様のMySQLデータベース設計に役立つことを願っています。

以下もご興味があるかもしれません:
  • MySQL における楽観的ロックと悲観的ロックの例
  • MySQL の悲観的ロックと楽観的ロックの使用例
  • MySQL における楽観的ロック、悲観的ロック、MVCC の包括的な分析
  • MySQL における悲観的ロックと楽観的ロック
  • MySQL 悲観的ロックと楽観的ロックの実装

<<:  iptables の再起動後に Docker の iptables ルールの完全なプロセスが失われる

>>:  Vueを使用して天気コンポーネントをロードする方法の詳細な説明

推薦する

Nginx は高可用性クラスタ構築を実装します (Keepalived+Haproxy+Nginx)

1. コンポーネントと実装機能Keepalived: Haproxy サービスの高可用性を実現し、...

JavaScriptの動作メカニズムの詳細な説明とイベントループについての簡単な説明

目次1. JavaScript がシングルスレッドなのはなぜですか? 2. タスクキュー3. イベン...

Reactでaxiosを使用してリクエストを送信する一般的な方法

目次Reactにaxios依存関係をインストールして導入するGETリクエストにaxiosを使用するa...

MacでMysqlのルートパスワードを忘れた場合の解決方法

長い間コンピューターで mysql を使用していなかったので、パスワードを忘れてしまいました。でも、...

Vue 父子価値移転、兄弟価値移転、子父価値移転の詳細な説明

目次1. 親コンポーネントが子コンポーネントに値を渡す1. 親コンポーネント.vue 2. サブコン...

1 時間で MySQL データベースを学ぶ (Zhang Guo)

目次1. データベースの概要1.1 開発の歴史2. MySQL の紹介2.1. MySQLの概要2....

MySQLデータベースのストアドプロシージャとトランザクションの違い

トランザクションは、複数の SQL ステートメントの原子性、つまり、それらが一緒に完了するか、一緒に...

Linux 脆弱性スキャンツール lynis の使用分析

はじめに: Lynis は、徹底的なセキュリティ スキャンを実行できる Unix システム用のセキュ...

vue3.0 でカルーセル コンポーネントをカプセル化する手順

目次1: カプセル化の考え方2. 包装工程3: ドットインジケーター4: 左と右のインジケーター5:...

MySQLが内部一時テーブルを使用するタイミングについて簡単に説明します。

組合執行分析を簡単にするために、次のSQLを例として使用します。 テーブル t1 を作成します ( ...

異なるデータベースで DROP TABLE を書く方法

異なるデータベースで DROP TABLE を書く方法1.MySQL 存在する場合はテーブルを削除 ...

Dockerfile テキストファイルの使用例の分析

Dockerfile は、イメージをビルドするために使用されるテキスト ファイルです。テキスト コン...

広告を閉じる効果を実現するJavascript

参考までに、Javascript を使用して広告を閉じる方法に関するケース スタディを示します。詳細...

OpenSSL を使用した Kubernetes 証明書の生成の概要

Kubernetes は、基本認証、トークン認証、CA 認証の 3 種類の認証をサポートしています。...

共有サイドバーを実装するためのネイティブJS

この記事では、ネイティブ JS で実装された共有サイドバーを紹介します。効果は次のとおりです。 以下...