spring事務和加可重入鎖解決庫存超賣遇到的問題
阿新 • • 發佈:2021-01-17
技術標籤:JAVA
想法:使用可重入鎖實現預鎖庫存,防止超賣現象,資料庫中存入100件商品,測試時每次購買一件,預鎖定一件。理想狀態是無超賣現象。
@PostMapping("/buyLock") @Transactional(rollbackFor = Exception.class,isolation = SERIALIZABLE) public String buyLock(Integer quantity,Long skuId){ lock.lock(); try { PmsSkuStock skuStock = skuStockMapper.selectByPrimaryKey(skuId); int left = skuStock.getStock() - skuStock.getLockStock(); if (left < quantity){ return "庫存不足"; } int s = skuStockMapper.updateLockStock(skuId,quantity); if (s == 1){ return "購買成功"; } }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } return "購買失敗"; }
結果發生了超賣,實際鎖定了101件。
解決方法一:去掉@Transactional註解,再試,沒有超賣了
@PostMapping("/buyLock") public String buyLock(Integer quantity,Long skuId){ lock.lock(); try { PmsSkuStock skuStock = skuStockMapper.selectByPrimaryKey(skuId); int left = skuStock.getStock() - skuStock.getLockStock(); if (left < quantity){ return "庫存不足"; } int s = skuStockMapper.updateLockStock(skuId,quantity); if (s == 1){ return "購買成功"; } }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } return "購買失敗"; }
解決方法二:加上@Transactional註解,將鎖提到事務外面,沒有超賣
@PostMapping("/buyLock") public String buyLock(Integer quantity,Long skuId){ lock.lock(); try { return buyLock1(quantity,skuId); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } return "購買失敗"; } @Transactional(rollbackFor = Exception.class) public String buyLock1(Integer quantity,Long skuId){ PmsSkuStock skuStock = skuStockMapper.selectByPrimaryKey(skuId); int left = skuStock.getStock() - skuStock.getLockStock(); if (left < quantity){ return "庫存不足"; } int s = skuStockMapper.updateLockStock(skuId,quantity); if (s == 1){ return "購買成功"; } return "購買失敗"; }
解決方法三:spring事務預設使用mysql預設的事務隔離級別,是可重複讀,將spring事務級別設定成序列化,沒有超賣
@PostMapping("/buyLock")
@Transactional(rollbackFor = Exception.class,isolation = SERIALIZABLE)
public String buyLock(Integer quantity,Long skuId){
lock.lock();
try {
PmsSkuStock skuStock = skuStockMapper.selectByPrimaryKey(skuId);
int left = skuStock.getStock() - skuStock.getLockStock();
if (left < quantity){
return "庫存不足";
}
int s = skuStockMapper.updateLockStock(skuId,quantity);
if (s == 1){
return "購買成功";
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
return "購買失敗";
}
結論:
發生上述現象的原因是,事物未提交之前,鎖已經釋放(事物提交是在整個方法執行完),導致另一個事物讀取到了這個事物未提交的資料