mysql悲觀鎖處理贈品庫存超賣的情況
阿新 • • 發佈:2018-05-27
count func this set private flex tail and 預測
處理庫存超賣的情況前,先了解下什麽是樂觀鎖和悲觀鎖,下面的幾篇博客已經介紹的比較詳細了,我就不在贅述其原理了
【MySQL】悲觀鎖&樂觀鎖
對mysql樂觀鎖、悲觀鎖、共享鎖、排它鎖、行鎖、表鎖概念的理解
下面開始介紹悲觀鎖在實際中的應用了
//下訂單
..........
try {
M()->startTrans();
//判斷商品是否有贈品 $give_gift=$ob->getGiveGoods($sku_nos); if(!empty($give_gift)){ $this->dealSkuGift($give_gift,$ob,$data[‘order_no‘],$skuNumArr,$packageSku); }
}catch(FlException $ex) {
M()->rollback();
$curCode = $ex->getErrorCode();
$curmsg = $ex->getMessage();
E($curCode, $curmsg);
}
M()->commit();
..........
//獲取贈品庫存
public function getGiveGoodsStock($sku_no){ $sku_no=implode(",",array_unique($sku_no)); $sku_no = "‘".str_replace(",","‘,‘",$sku_no)."‘"; $Model = new \Think\Model(); $sku_gift_info=$Model->query("select sku_no,stock from ".C(‘DB_PREFIX‘)."sku_gift_stock where is_deleted=0 and is_online=1 and sku_no in($sku_no) for update"); return$sku_gift_info; }
// 處理贈品
private function dealSkuGift($give_gift,$ob,$orderNo,$skuNumArr,$packageSku){ if(empty($give_gift) or empty($skuNumArr)){ return false; } $skuGiftStockM=M(‘sku_gift_stock‘); $sku_gifts=array_column($give_gift,‘sku_no‘); //獲取贈品庫存 $sku_give_gift=$ob->getGiveGoodsStock($sku_gifts,1); $skuGiftStock=array(); $giftCount=0; foreach($sku_give_gift as $k=>$v){ $skuGiftStock[$v[‘sku_no‘]]=$v[‘stock‘]; } $public = new PModel(); foreach ($give_gift as $v){ $goodsNums=0; $delStockWhere=array(); if($v[‘isHaveStock‘]==‘1‘ && $skuGiftStock[$v[‘sku_no‘]] >0){//代表有貨--這些是需要發貨的 if($v[‘type‘]==‘1‘){ $lastGiftNum=$skuNumArr[$v[‘parent_sku_no‘]]*$v[‘goods_nums‘]; }else{//按固定贈品數量 $lastGiftNum=$v[‘goods_nums‘]; } if($lastGiftNum >= $v[‘stock_num‘]){ $lastGiftNum=$v[‘stock_num‘]; } if($v[‘stock_num‘]==0){ $lastGiftNum=0; } $delStockWhere[‘sku_no‘]=$v[‘sku_no‘]; $delStockWhere[‘is_deleted‘]=0; $delStockWhere[‘is_online‘]=1; $delStockWhere[‘_string‘]= sprintf(‘stock>=%d‘, $lastGiftNum); $skuGiftStockM->where($delStockWhere)->lock(true)->setDec(‘stock‘,$lastGiftNum);//扣除商品贈品庫存 $re_v=$public->updataSignData($v[‘sku_no‘],$lastGiftNum,‘virtual_inventory‘,‘-‘,‘goods_sku‘,‘sku_no‘);//扣除贈品商品庫存 if(false == $re_v){ E(‘300110‘); } $giftCount+=$lastGiftNum; }else{ $lastGiftNum=0; } $dd[]=array( ‘parent_sku_no‘=>$v[‘parent_sku_no‘], ‘sku_no‘=>$v[‘sku_no‘], ‘num‘=>$lastGiftNum,//最終發貨的數量,無貨為0 ‘create_time‘=>date(‘Y-m-d H:i:s‘,time()), ‘order_no‘=>$orderNo, ‘package_id‘=>$packageSku[$v[‘parent_sku_no‘]], ‘stock_num‘=>empty($skuGiftStock[$v[‘sku_no‘]])?0:$skuGiftStock[$v[‘sku_no‘]] ); } $order_goods_gift=M(‘order_goods_gift‘); $order_goods_gift->addAll($dd); $upOrdeData=array( ‘gift_count‘=>$giftCount+1 ); M(‘order‘)->where(‘order_no="‘.$orderNo.‘"‘)->data($upOrdeData)->save(); return true;
}
//接口訪問方式
//由於要測試並發下該接口的超賣處理情況,所以訪問接口前,可將body裏的參數寫死到接口內,這樣方便調試
//再次訪問接口
//下面就可以用apache的ab工具對下單接口進行並發測試了
到數據庫裏查看商品P002026-01關聯了2個贈品,各關聯了10個
查看贈品的庫存數量
apache並發測試的原理及使用方法參見博客:https://www.cnblogs.com/lishuyi/p/5808661.html
使用方法:
ab -n 10 -c 5 http://app.zouke.com/ (-n發出10個請求,-c模擬5個並發,相當5人同時訪問,後面是測試url) ab -t 60 -c 100 http://192.168.0.10/ 在60秒內發請求,一次100個請求。
先預測下,請求10次,並發5個,最終的庫存會剩余0
下面開始並發測試
最終贈品P002872、P002962的剩余庫存如下
結果可見,本次悲觀鎖起了效果,下面看下不用悲觀鎖的情況會是什麽樣,下面將2個贈品的庫存都恢復為10個,並將獲取贈品庫存代碼進行改造,這次獲取贈品庫存是沒加悲觀鎖的
//獲取贈品庫存 public function getGiveGoodsStock($sku_no,$ori){ $sku_gift=M(‘sku_gift_stock‘); $sgg_where[‘sku_no‘]=array(‘in‘,$sku_no); $sgg_where[‘is_deleted‘]=0; $sgg_where[‘is_online‘]=1; $sku_gift_info=$sku_gift->field(‘sku_no,stock‘)->where($sgg_where)->select(); return $sku_gift_info; }
再次並發測試一下
發現贈品庫存是變為負數了,這是超賣的情況出現了
好了,以上測試可以了,下面看下直接用操作數據表看下效果,按如下順序執行,發現當執行完前4步後,用戶B查詢不到贈品庫存信息,主要是因為此時贈品表已經鎖住了
下面執行用戶A第5步COMMIT,用戶B查詢贈品庫存立馬查詢出來了
上述可見,mysql悲觀鎖對贈品庫存超賣的處理流程的生效過程了
mysql悲觀鎖處理贈品庫存超賣的情況