庫存扣減和鎖
常見的實現方案:
- 程式碼同步, 例如使用 synchronized ,lock 等同步方法
- 不查詢,直接更新
update table set surplus = (surplus - buyQuantity) where id = xx and (surplus - buyQuantity) > 0
- 使用CAS,
update table set surplus = aa where id = xx and version = y
- 使用資料庫鎖,
select xx for update
- 使用分散式鎖(zookeeper,redis等)
1.程式碼同步, 使用 synchronized ,lock 等同步方法
publicsynchronizedvoidbuy(StringproductName,IntegerbuyQuantity){
//其他校驗...
//校驗剩餘數量
Productproduct=從資料庫查詢出記錄;
if(product.getSurplus<buyQuantity){
return"庫存不足";
}
//set新的剩餘數量
product.setSurplus(product.getSurplus()-quantity);
//更新資料庫
update(product);
//記錄日誌...
//其他業務...
}
先說下這個方案的前提配置:
- 使用spring 宣告式事務管理
- 事務傳播機制使用預設的(PROPAGATION_REQUIRED)
- 專案分層為controller-service-dao 3層, 事務管理在service層
這個方案不可行,主要是因為以下幾點:
1).synchronized 作用範圍是單個jvm例項, 如果做了叢集,分散式等,就沒用了
2).synchronized是作用在物件例項上的,如果不是單例,則多個例項間不會同步(這個一般用spring管理bean,預設就是單例)
3).單個jvm時,synchronized也不能保證多個數據庫事務的隔離性. 這與程式碼中的事務傳播級別,資料庫的事務隔離級別,加鎖時機等相關.
2.不查詢,直接更新
publicsynchronizedvoidbuy(StringproductName,IntegerbuyQuantity){
//其他校驗...
int影響行數=updatetablesetsurplus=(surplus-buyQuantity)whereid=1and(surplus-buyQuantity)>0;
if(result<0){
return"庫存不足";
}
//記錄日誌...
//其他業務...
}
這樣確實可以實現,不過有一些其他問題:
- 不具備通用性,例如add操作
- 庫存操作一般要記錄操作前後的數量等,這樣沒法記錄
- 其他...
3.使用CAS, update table set surplus = aa where id = xx and yy = y
publicvoidbuy(StringproductName,IntegerbuyQuantity){
//其他校驗...
Productproduct=getByDB(productName);
int影響行數=updatetablesetsurplus=(surplus-buyQuantity)whereid=1andsurplus=查詢的剩餘數量;
while(result==0){
product=getByDB(productName);
if(查詢的剩餘數量>buyQuantity){
影響行數=updatetablesetsurplus=(surplus-buyQuantity)whereid=1andsurplus=查詢的剩餘數量;
}else{
return"庫存不足";
}
}
//記錄日誌...
//其他業務...
}
4.使用資料庫鎖, select xx for update
publicvoidbuy(StringproductName,IntegerbuyQuantity){
//其他校驗...
Productproduct=select*fromtablewherename=productNameforupdate;
if(查詢的剩餘數量>buyQuantity){
影響行數=updatetablesetsurplus=(surplus-buyQuantity)wherename=productName;
}else{
return"庫存不足";
}
//記錄日誌...
//其他業務...
}
5.使用分散式鎖(zookeeper,redis等)
分散式鎖的實現方案有很多:基於redis,基於zookeeper,基於資料庫等等