ペシミスティック・ロック
ペシミスティック・ロック(ペシミスティック・ロック)は、定義により、非常に悲観的で、毎回データを拾うそれが取るまで、人々は、このデータを取りたいので、他の人は、そう、彼女はデータを取ったたびにロックされます、修正するブロックになると思いますロックします。
ペシミスティック・ロック:シールドは、すべての運用データの整合性に違反することも、同時実行違反が発生したと仮定します。
Javaは、唯一のスレッドがデータを操作することができ、他のスレッドがブロックされることを確実に、実装悲観的ロッキング、データを変更するロックを獲得するために、各スレッドに属する同期。
ペシミスティック・ロック
オプティミスティック・ロック(オプティミスティック・ロック)、定義により、非常に楽観的である、それはロックされませんので、他の人が変更されないたびにデータを拾うが、他の人が更新する必要はありませんかを判断します。この時間の間に更新提出する場合このデータ。オプティミスティック・ロックは、シナリオを書くためにどのくらいの読み取りに適用され、これは、スループットを向上させることができます
オプティミスティック・ロック:と仮定同時実行の競合が発生していないが、ちょうどコミット操作時にチェックすることで、データの整合性に違反しています
一般的な次の2つの方法でオプティミスティック・ロック:
- 楽観的ロックの実装の最も一般的な種類である、達成するためにデータのバージョン(版)記録メカニズムを使用します。データのバージョンは何?すなわち、データ増加のバージョン識別子は、一般的にデータベーステーブルのデジタルタイプ「バージョン」フィールドを追加することによって達成されます。データを読み出す場合、フィールドのバージョンの値を1つたびに、このバージョンを更新されたデータを読み出します。我々は、比較のために記録された値に対応するバージョンの取り出し最初でデータベーステーブルの現在のバージョンを確認するための情報を更新し送信すると、取り出された第一の値に等しい、データベーステーブルのバージョン番号の現在のバージョン場合、次に、更新されますそれ以外の場合は古いデータを検討しました。
- タイムスタンプ(タイムスタンプ)を使用します。第2の実施楽観的ロックと最初にフィールドを追加するために必要なテーブル楽観的ロック制御においてほぼ同じで、名前は重要ではありません、フィールド・タイムスタンプ(タイムスタンプ)を使用してタイプ、および上記のバージョンは類似しているだけでなく、更新に提出しますあなたは、そうでない場合は、バージョンの競合をデータベースに現在のタイムスタンプデータをチェックして、同じことがOKであれば、タイムスタンプ比較に取る前に自分自身を更新することができます。
アトミックパッケージの一種は、JavaのJUC実装でCAS(比較およびSET)を介して、スレッドセーフな操作を実現するためのAtomicInteger増分楽観的ロックです。
MySQLのInnoDBは二相ロック・プロトコル(ツーフェーズロック・プロトコル)を使用します。トランザクション中に、ロックがいつでも実行することができ、ロックはCOMMITまたはROLLBACKときにのみ放出され、すべてのロックが同時にリリースされています。ロックの前には、暗黙的にロックされたトランザクション分離レベルに基づいて必要なときに、InnoDBは自動的にロックされます説明されています。
さらに、InnoDBはまた、SQL仕様に属していない特定のステートメントによって表示ロックをサポートしています。
- 共有モードでSELECT ... LOCK
- SELECT ... FOR UPDATE
ケース
小さい場合によって楽観と悲観的ロックの使用を実証
商品は買わない場合は、データの整合性を確保することを保証するために、電力供給のスパイクシステムを考えてみましょう
製品テーブルと仮定
create table tb_product(
id int not null auto_increment primary key,
stock int not null
)ENGINE=InnoDB DEFAULT CHARSET=utf8
次のように同時変更の商品在庫の擬似コードを考慮せずに:
/**
* 更新库存(不考虑并发)
* @param productId
* @return
*/
public boolean updateStockRaw(Long productId){
ProductStock product = query("SELECT * FROM tb_product WHERE id=#{productId}", productId);
if (product.getNumber() > 0) {
int updateCnt = update("UPDATE tb_product SET stock=stock-1 WHERE id=#{productId}", productId);
if(updateCnt > 0){ //更新库存成功
return true;
}
}
return false;
}
しかし、このアプローチは、複数の並行スレッドの場合には売られ過ぎの問題が表示されることがあります。
以下は、この問題を解決するための使用悲観的ロックと楽観的ロックを示しています。
悲観的ロックを使用します
/**
* 更新库存(使用悲观锁)
* @param productId
* @return
*/
public boolean updateStock(Long productId){
//先锁定商品库存记录
ProductStock product = query("SELECT * FROM tb_product WHERE id=#{productId} FOR UPDATE", productId);
if (product.getNumber() > 0) {
int updateCnt = update("UPDATE tb_product SET stock=stock-1 WHERE id=#{productId}", productId);
if(updateCnt > 0){ //更新库存成功
return true;
}
}
return false;
}
楽観的ロックを使用します
/**
* 下单减库存
* @param productId
* @return
*/
public boolean updateStock(Long productId){
int updateCnt = 0;
while (updateCnt == 0) {
ProductStock product = query("SELECT * FROM tb_product WHERE product_id=#{productId}", productId);
if (product.getNumber() > 0) {
updateCnt = update("UPDATE tb_product SET stock=stock-1 WHERE product_id=#{productId} AND number=#{number}", productId, product.getNumber());
if(updateCnt > 0){ //更新库存成功
return true;
}
} else { //卖完啦
return false;
}
}
return false;
}