電子商取引システムにおける在庫の減算は非常に重要な操作です。たとえば、フラッシュ セール システムでは、過剰販売を防止する必要があります。販売者が 100 個の在庫を設定したにもかかわらず、最終的に 1,000 個を販売した場合、経済的損失が発生します。在庫を差し引く場合、通常、次のステートメントが使用されます。
udpate goods set stock = stock - #{acquire} where sku_id = #{skuId} and stock - #{acquire} >= 0
在庫リソースを保護するために、このステートメントが在庫の過剰販売を効果的に防ぐ方法を分析してみましょう。この記事のデモでは、MySQL Innodb エンジンを使用し、分離レベルを反復読み取りに設定します。
共有ロック (share Lock) は読み取りロックとも呼ばれ、共有ロックを実装するステートメントは次のとおりです:
select lock in share mode
排他ロック(排他ロック) は書き込みロックとも呼ばれます。排他ロックを実装するステートメントは次のとおりです:
select for update update delete insert
共有ロックと排他ロックの互換性関係は次のとおりです:
us 例を通じて上記の互換性関係を分析します。まず、テスト テーブルを作成し、テスト データを書き込みます:
CREATE TABLE `test_account` ( `id` bigint(20) NOT NULL, `name` varchar(20) DEFAULT NULL, `account` bigint(20) DEFAULT NULL, `version` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into `test_account`(`id`,`name`,`account`,`version`) values (1,'A',100,1); insert into `test_account`(`id`,`name`,`account`,`version`) values (2,'B',200,1); insert into `test_account`(`id`,`name`,`account`,`version`) values (3,'C',300,1);
(1) 互換性の読み取りと読み取り
#共有ロックと共有ロック 次の例では、セッション 1 は時刻 t3 にクエリを実行でき、セッション 2 は時刻 t4 にクエリを実行することで期待される結果を取得できます。(2) 読み取りと書き込みの相互排他
共有ロックと排他的ロックは相互に排他的です。次の例では、セッション 1 が時刻 t3 に共有ロックを追加し、結果を正しく読み取ることができます。 , しかし、セッション 2 は時刻 t4 に排他ロックを追加しようとしますが、これ ロックがセッション 1 によって占有されている場合、セッション 2 は待機する必要があります。セッション 1 が長時間ロックを解放しない場合、セッション 2 はロック タイムアウト例外をスローします:
(3) 書き込みと書き込みの相互作用の除外
排他的ロックと排他的ロックは相互に排他的です。次の例では、session1 によって排他的ロックが追加されます。時刻 t3 でロックが解除され、結果は正しく読み取られますが、セッション 2 は時刻 t4 で排他ロックを追加しようとしますが、この時点ではロックはセッション 1 によって占有されており、セッション 2 は待機する必要があります。長時間ロックすると、セッション 2 はロック タイムアウト例外をスローします:
1.2 現在の読み取り値とスナップショットの読み取りMySQL Innodb ストレージ エンジンは、マルチ-version 同時実行制御プロトコル MVCC: MVCC 同時実行制御では、読み取り操作はスナップショット読み取りと現在の読み取りに分割できます。
スナップショットの読み取りにはロックが必要ありません。読み取られるのはレコードの表示可能なバージョンであり、これは履歴バージョンである可能性があります。注文スナップショットと同様に、ユーザーが注文した後に製品の価格が変更された場合でも、注文スナップショットは変更されません。現在の読み取りステートメントは次のように実装されます。
select
select lock in share mode select for update update delete insert
例を通じてスナップショット読み取りと現在の読み取りを分析します。セッション 2 は t4 でレコードを変更し、t5 で送信しました。セッション 1 は t6 でスナップショット読み取りを実行し、これを読んでください トランザクション開始時の結果は 100 でした。現在の読み取りは t7 で実行され、結果の最新バージョン 101 が読み取られました:
現在の読み取り値は何ですかみたいなプロセス?現在の読み取りプロセスを分析するための例として更新を取り上げます。 プログラム インスタンスが現在の読み取りリクエストを初めて発行すると、ストレージ エンジンは条件を満たす最初のレコードを返します。 where 条件を指定してロックすると、プログラム インスタンスが更新リクエストを発行し、ストレージによって操作が正常に応答を完了します。 where 条件を満たすすべてのレコードが実行されるまで、順番に実行されます。 ここでいくつかの拡張を行います。RR レベルでは、ファントム読み取りの問題を回避するための 2 つのメカニズムが提供されます: 1 つ目の方法はスナップショット読み取りで、現在のトランザクションの開始時にスナップショットを読み取ります。現在の読み取りの 1 つの方法は、Next-Key Lock メカニズムを使用してファントム読み取りを防ぐことです。2 楽観的ロックの原則
質問を通じて上記の知識を統合します: 次のステートメントを同時に実行する 2 つのスレッドがあります。レコード ID=1 のアカウント値は成功しますか? ?控除が2回?
update test_account set account = account - 100, version = version + 1 where id = 1 and version = 1
上記のステートメントはオプティミスティック ロックを使用しています。オプティミスティック ロックがリソースを保護することはわかっているので、答えは 2 回減算しないことですが、そこで止まるわけにはいきません。さらに分析するには、第 1 章の知識を組み合わせる必要があります。 :
時刻 t2 では、セッション 1 とセッション 2 が更新操作を同時に実行します。更新により排他ロックが追加されるため、2 つのうち 1 つだけが成功します。セッション 1 は成功します。 、セッション 2 はブロックし、キューがキューに追加されるのを待ち、ロックを解放します。時刻 t3 で、セッション 1 が排他ロックを解除するトランザクションをコミットします。このとき、セッション 2 が現在の読み取り用のロックを取得しますが、この時点で id=1 のレコードのバージョン値が 2 になり、実行されたステートメントは更新するデータをクエリできないため、レコードは更新されません。
第 2 章の楽観的ロックの原則を理解していれば、在庫控除の原則はすでに明らかです。在庫が 1 個だけ残っていると仮定します。スレッドが 2 つある場合 同時に在庫を減らすと売れすぎが発生しますか?
#時刻 t2 で、セッション 1 とセッション 2 が同時に在庫を削減するために updatek を実行します。更新では排他ロックが追加されるため、2 つのうち 1 つだけが成功します。セッション 1 は成功します。 、セッション 2 はブロックして待機し、排他的ロックが解放されます。 時刻 t3 で、セッション 1 がトランザクションをコミットして排他ロックを解放します。このとき、セッション 2 が現在の読み取り用のロックを取得しますが、この時点で製品 1 の在庫は 0 になり、在庫はなくなります。満たされています (在庫 - 1 >= 0) 条件: 実行ステートメントは更新するデータをクエリできないため、レコードは更新されません。以上がMySQL における楽観的ロック控除インベントリの原理は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。