1. 程式人生 > >mysql悲觀鎖處理贈品庫存超賣的情況

mysql悲觀鎖處理贈品庫存超賣的情況

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悲觀鎖處理贈品庫存超賣的情況