MySQLの悲観的ロックと楽観的ロックの実装スキーム
前書き
悲観的ロックと楽観的ロックは、並行処理の問題を解決するために使われる2つの考え方で、それぞれ異なるプラットフォームで独自の実装がなされています。例えば、Javaではsynchronizedは悲観的ロックの実装(厳密ではなく、ヘビーウェイトロックにアップグレードする際にカウントされるロックアッププロセスがある)、Atomic***アトミッククラスは楽観的ロックの実装と考えることができる。
悲観的なロック
強い排他性と排他性を持ち、通常はシステムの相互排他によって、処理中もデータをロック状態に保つ。他のスレッドがロックを取得しようとすると、ロックを持っているスレッドがそれを解放するまでブロックされます。
楽観的ロック
データの変更とアクセスについて、競合が発生しないことを前提に楽観的に考える。データが更新のために送信されるときだけ、データの競合があるかどうかがテストされ、競合がなければ更新は成功し、そうでなければすぐに失敗し、ユーザーにエラーを返して次に何をするか選択させます。
MySQL 自身がロック機構をサポートしています。例えば、quot;check before write"の要件がある場合、処理全体を途中で中断できないアトミック操作にしたいのですが、これはクエリデータ行に "exclusive lock" を追加することで実現可能です。現在のトランザクションがロックを解放しない限り、MySQL は他のトランザクションが排他ロックを取得したい場合、現在のトランザクションがロックを解放するまでブロックします。この MySQL の排他的ロックは、「悲観的ロック」と呼ばれています。
MySQL 自体は楽観的ロック機能を提供していないため、開発者が自ら実装する必要があります。一般的な方法は、テーブルにバージョンカラムを追加し、データ行のバージョンをマークすることです。データを更新する必要がある場合、バージョンのバージョンを比較する必要があり、バージョンが一致していれば、この期間に他のトランザクションによってデータが変更されていないことを意味し、そうでなければ、他のトランザクションによってデータが変更されたことを意味し、スピンの再試行が必要となります。
ハンズオン
データベースには、商品テーブルと注文テーブルの2つのテーブルがあるとします。
ユーザーが注文した後、2つのアクションを実行する必要があります。
- 商品テーブルが在庫から減算される。
- Orders テーブルにレコードが作成されます。
初期データ:ID1の商品の在庫は100個で、注文テーブルのデータは空である。
クライアントは同時に発注するために10個のスレッドを起動し、それぞれロックフリー、悲観的ロック、楽観的ロックのシナリオでどのように動作するかを確認します。
以下は、テーブルを作成するためのSQL文です。
-- Goods table
CREATE TABLE `goods` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`goods_name` varchar(50) NOT NULL,
`price` decimal(10,2) NOT NULL,
`stock` int(11) DEFAULT '0',
`version` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
-- order table
CREATE TABLE `t_order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`goods_id` bigint(20) NOT NULL,
`order_time` datetime NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
1. ロックなし
加工がない
// Place an order
private boolean order(){
Goods goods = goodsMapper.selectById(1L);
boolean success = false;
if (goods.getStock() > 0) {
goods.setStock(goods.getStock() - 1);
// Update the inventory
goodsMapper.updateById(goods);
// Create an order
orderMapper.save(goods.getId());
success = true;
}
return success;
}
コンソール出力の結果
2.悲観的なロック
FOR UPDATEで項目を照会すると、データ行に排他的ロックがかかるので、他のスレッドが再度照会すると、現在のスレッドのトランザクションがコミットしてロックが解放され、他のスレッドが継続して発注できるようになるまでブロックされます。この方法は、高い同時実行性能を持ちません。
SQLステートメント
@Select("SELECT * FROM goods WHERE id = #{id} FOR UPDATE")
Goods selectForUpdate(Long id);
コンソール出力です。
注:FOR UPDATEはトランザクション内でなければ有効ではなく、クエリと更新は同じトランザクション内でなければなりません!!!
3. 最適化されたロック
更新のたびにバージョン番号をチェックし、バージョン番号が同じであれば、その期間中に他のスレッドによってデータが変更されていないので、現在のスレッドは普通に更新を送信することができます。
データを更新している間、バージョン番号は自己インクリメントでなければならない!!!!
@Update("UPDATE goods SET stock = #{stock},version = version+1 WHERE id = #{id} AND version = #{version}")
int updateByVersion(Long id, Integer stock, Integer version);
ビジネスコード
boolean order(){
Goods goods = goodsMapper.selectById(1L);
boolean success = false;
if (goods.getStock() > 0) {
goods.setStock(goods.getStock() - 1);
// Update the stock with the version number
int result = goodsMapper.updateByVersion(goods.getId(), goods.getStock(), goods.getVersion());
if (result <= 0) {
// The update failed, indicating that the data has been modified by other threads during the period and needs to be retried recursively
return order();
}
// Create an order
orderMapper.save(goods.getId());
success = true;
}
return success;
}
コンソール出力の結果
概要
この記事は、MySQLの悲観的なロックと楽観的なロック方式を紹介し、より関連するMySQLの悲観的なロックと楽観的なロック内容は、スクリプト家の過去の記事を検索してくださいまたは次の関連記事を閲覧を継続し、あなたが将来的にスクリプト家をよりサポートすることを願っています
関連
-
MySQLデータベースでvarchar型の数値の大きさを比較する方法
-
MySQLデータ型の詳細
-
MySQL サービスとデータベース管理
-
MySql認証ベースのvsftpd仮想ユーザー
-
MySQL インタビューの質問 - ハッシュインデックスを設定する方法
-
MysqlからElasticsearchにデータを同期させる方法を説明します。
-
[解決済み】ValueError: 値の長さがインデックスの長さと一致しない|Pandas DataFrame.unique()
-
[解決済み】マルチパート識別子をバインドできない
-
Mysqlデータベースへのリンク時にpymysqlがConnection refusedエラーを報告する
-
mysql がエラーを報告します。不明な文字セットです。'utf8mb4'
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
SQL基本クエリステートメント
-
SpringBootのMySQLへの接続は、バックエンドのインターフェイスの操作方法を書き込むためのデータを取得するために
-
MySQLによる既存テーブルのパーティショニングの実装
-
Mysqlのソート機能の詳細
-
[解決済み】DynamoDB : 提供されたキー要素がスキーマと一致しません。
-
[解決済み】SQLサーバーのテンポラリーテーブルで「すでに名前のついたオブジェクトがあります」エラーが発生する。
-
mysql5.7のインストールと、無料・長期利用を目的としたNavicateの導入プロセスについて
-
MySQLにおけるvarchar型とchar型の違い
-
MySQLの一般的な分割ライブラリおよび分割テーブルスキームの概要
-
Mysql がエラーを報告 オペランドには 1 つのカラムが含まれている必要があります。