mysql悲观锁处理赠品库存超卖的情况 【MySQL】悲观锁&乐观锁

处理库存超卖的情况前,先了解下什么是乐观锁和悲观锁,下面的几篇博客已经介绍的比较详细了,我就不在赘述其原理了

【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悲观锁对赠品库存超卖的处理流程的生效过程了

猜你喜欢

转载自www.cnblogs.com/zouke1220/p/9096077.html