• 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 ThinkModel();
        $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悲观锁对赠品库存超卖的处理流程的生效过程了

  • 相关阅读:
    Codeforces 631A Interview【模拟水题】
    Codeforces 651E Table Compression【并查集】
    Codeforces 651D Image Preview【二分+枚举】
    Codeforces 651C Watchmen【模拟】
    Codeforces 651B Beautiful Paintings【贪心】
    18.06.26 16年期末10:游览规划
    18.06.25 POJ4129 16年期末09:变换的迷宫
    18.06.25 POJ4150 16年期末07:上机
    18.06.25 16年期末06 42点
    18.06.25 16年期末01-05集合
  • 原文地址:https://www.cnblogs.com/zouke1220/p/9096077.html
Copyright © 2020-2023  润新知