mysql悲观锁处理赠品库存超卖的情况
Posted 邹柯
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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悲观锁处理赠品库存超卖的情况的主要内容,如果未能解决你的问题,请参考以下文章