電商項目中下單是要減庫存的,但是當多個用戶同時請求時會產生併發,導致多個線程同時減庫存,庫存可能變爲負數,在這裏我記一下一個簡單的防止超賣的方法。
JAVA代碼:
private ResultDTO<Object> checkAndUpdateInventory(SampleInventoryDTO dto, int index) throws InterruptedException {
if (index > 15) {
throw new BusinessException("同時操作人數過多,請稍後再試!", DataContract.SAMPLE_INVENTORY_UPDATE_TOOMUCH_CODE);
}
if (index >= 3) {
//如果三次以上都沒有更新成功則睡眠100毫秒
Thread.sleep(100);
}
//根據商品ID查詢庫存
Integer inventory = productDao.queryInventory(dto.getProductNo());
ProductVO vo = new ProductVO ();
vo.setProductNo(dto.getProductNo());
vo.setQuantity(dto.getQuantity());
vo.setInventory(inventory);
if (inventory >= dto.getQuantity()) {
if (productDao.updateQuantityByVersion(vo) < 1) {
return checkAndUpdateInventory(dto, ++index);
} else {
return ResponseUtil.generateSuccessDTO("修改庫存成功.");
}
} else {
return ResponseUtil.generateFaileDTO("商品庫存不足!", DataContract.SAMPLE_INVENTORY_NOT_ENOUGH_CODE);
}
}
再貼上關鍵的SQL代碼(productDao.updateQuantityByVersion() 的SQL):
UPDATE SAMPLE
SET
INVENTORY= INVENTORY - #{quantity}
WHERE RECORD_NO=#{sampleNo} AND INVENTORY = #{inventory} AND INVENTORY >= #{quantity}
整體思路就是在更新庫存之前先查詢一下庫存,判斷是不是足夠且有沒有變動過,在執行更新SQL庫存時如果庫存不等於查出來的庫存,則表示有其它線程修改了庫存,因此進入遞歸重新查詢庫存,直到更新庫存SQL執行成功,這裏使用了quantity庫存數量代替了樂觀鎖的版本號,個人感覺相比較排它鎖的性能慢,和隊列的麻煩,這個方法最簡單,當然,要是項目併發很高的話還是推薦用列隊比較好。