悲観ロック楽観ロック

3731 ワード

悲観的なロックと楽観的なロックは人々が定義した概念であり、あなたは思想として理解することができ、同時資源を処理する常用手段である.これはmysqlで提供されるロックメカニズム(表ロック,行ロック,排他ロック,共有ロック)とは異なり,後者は前者の実現手段となる.
悲観ロック
その名の通り、データの処理に悲観的な態度をとり、常に衝突が発生し、データを取得し、修正すると、他の人がデータを修正すると考えられています.したがって、データ処理全体でデータをロックする必要があります.悲観的なロックの実装は、mysqlの排他的ロック、for updateなどのデータベースが提供するロックメカニズムによって実現されることが多い.
例:商品の秒殺過程で、在庫数が$num減少し、超過販売を避ける.
CREATE TABLE 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'更新時間',PRIMARY KEY(id),UNIQUE KEY goods_id(goods_id))ENGINE=InnoDB DEFAULT CHARSET=utf 8 mb 4 COMMENT='商品在庫表;商品在庫数numsフィールドタイプをunsignedに設定し、データベースレベルで負数が発生しないことを保証します.
悲観的なロックを使用するにはmysqlの自動コミット機能をオフにし、set autocommit=0にする必要があります.
mysqlのロー・レベル・ロックはインデックスに基づいており、sqlがインデックスを外していない場合は、テーブル・レベル・ロックを使用してテーブル全体をロックします.
事務を開き、売る商品を調べ、その記録に鍵をかけます.
begin; select nums from tb_goods_stock where goods_id = {$goods_id} for update;
商品の数量が購入数量より大きいかどうかを判断する.満たされない場合は、トランザクションをロールバックします.
条件が満たされている場合は、受注を生成し、在庫を減らし、トランザクションをコミットします.
insert into ----
update tb_goods_stock set nums = nums - {$num} where goods_id = {$goods_id} and nums >= {$num};
commit; 悲観的なロックは、同時制御では、まずロックしてからデータを処理する保守的なポリシーを採用しており、データ処理の独占性を保証しているが、効率も低下しており、特にfor updateクエリの場合、すべてのスキャンされたロー(このカラム情報にインデックスがない場合は、テーブル全体をロックする)がロックされ、この場合、高同時性の場合は絶対に受け入れられない.
楽観ロック
楽観ロックは悲観ロックに対して、データの処理に楽観的な態度を持っており、データが一般的に衝突しないと考えているため、データが更新されたときに、正式にデータの衝突の有無を検出することができ、一般的にバージョン番号を利用して操作する.競合が見つかった場合は、エラーメッセージを返し、ユーザーにどのようにするかを決定させます.次に、データテーブルとキャッシュに楽観的にロックされている実装を見てみましょう.
CREATE TABLE 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'バージョン番号', PRIMARY KEY ( id ), UNIQUE KEY goods_id ( goods_id ) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf 8 mb 4 COMMENT='商品在庫表';
次の操作を行います.
begin;
1.在庫が足りているかどうかを判断するが、ここでは在庫があるかどうかを見るために、ない場合はできるだけ早くエラーを返すが、最終的に成功するかどうかを決めない
select nums, version from tb_goods_stock where goods_id = {$goods_id};
2.受注の生成
insert into ----
3.条件が満たされた場合、在庫を減らす.(更新時に現在のバージョンと第1ステップで取得したバージョンが同じか否かを判断)
update tb_goods_stock set nums = nums - {$num}, version = version + 1 where goods_id = {$goods_id} and version = {$version} and nums >= {$num};
4.更新操作が正常に実行されたかどうかを判断し、成功した場合はコミットし、そうでない場合はロールバックします.
楽観的なロックは、読み取りの多いアプリケーションシーンに適しています.しかし、バージョン番号の場合、コンカレント量が発生すると競合が頻繁に発生し、上位アプリケーションはユーザーに再操作を継続させ、かえってパフォーマンスが低下し、在庫の場合は別の解決策があります.
begin; select (inventory) from items where id=100;
1.在庫が足りているかどうかを判断するが、ここでは在庫があるかどうかを見るために、ない場合はできるだけ早くエラーを返すが、最終的に成功するかどうかを決めない
2.上で在庫があると判断した場合、注文書を生成する
insert into ----
3.条件が満たされた場合、在庫を減らす.(更新時に在庫が現在の在庫より大きいかどうかを判断し、バージョン番号に追加しない)
update tb_goods_stock set nums = nums - {$num} where goods_id = {$goods_id} and nums >= {$num};
4.更新操作が正常に実行されたかどうかを判断し、成功した場合はコミットし、そうでない場合はロールバックします.
この場合、ほぼ解決する同時時の問題です.
注意:データを更新するときは、在庫を計算してから数値を更新するのではなく、必ず計算と更新を1つの文に書いて原子操作にしてください.