page contents

商品超卖问题,你用PHP如何解决?

商场活动秒杀超卖问题,实际上是商品抢购活动,如果一个商品中有100件库存,但是在抢购过程中有1000人来抢购,那么就会出现并发情况, 最初只有100个库存,但要抢购,人数过多,数据库中将只有1...

商场活动秒杀超卖问题,实际上是商品抢购活动,如果一个商品中有100件库存,但是在抢购过程中有1000人来抢购,那么就会出现并发情况, 最初只有100个库存,但要抢购,人数过多,数据库中将只有100个库存,但是当库存为0时也是有人会成功提交。这是超卖问题。
这篇文章是简单的用redis队列来解决超卖问题。redis有list类型,而list类型一个双向链表。我们可以通过push和pop操作从链表的头部或者尾部添加或者删除元素。这么一来list既可以用作栈,也可以用作队列。这里就不对队列做更多的解释。这用redis就解决了并发的问题,在队列里先进先出,有效的解决超卖的问题。


如何设计表?
这里新建三张表: store商品表

CREATETABLE`store`(`id`int(11)NOTNULLAUTO_INCREMENT,`goods_id`int(11)NOTNULL,`sku_id`int(10)unsignedNOTNULLDEFAULT'0',`number`int(10)NOTNULLDEFAULT'0',`freez`float(11,2)NOTNULLDEFAULT'0.00'COMMENT'虚拟库存',`price`int(10)NOTNULLCOMMENT'价格:单位为分',PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=1DEFAULTCHARSET=utf8COMMENT='库存表';


order订单表

CREATETABLE`order`(`id`int(11)NOTNULLAUTO_INCREMENT,`order_sn`char(32)NOTNULL,`user_id`int(11)NOTNULL,`status`int(11)NOTNULLDEFAULT'0',`goods_id`int(11)NOTNULLDEFAULT'0',`sku_id`int(11)NOTNULLDEFAULT'0',`number`int(11)NOTNULL,`price`int(10)NOTNULLCOMMENT'价格:单位为分',`create_time`timestampNOTNULLDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=1DEFAULTCHARSET=utf8COMMENT='订单表';


log日志表

CREATETABLE`log`(`id`int(11)NOTNULLAUTO_INCREMENT,`event`varchar(255)NOTNULL,`type`tinyint(4)NOTNULLDEFAULT'0',`addtime`timestampNOTNULLDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(`id`))ENGINE=MyISAMAUTO_INCREMENT=1DEFAULTCHARSET=utf8;


如何实现呢?
首先将库存循环lpush进入redis值,比如goods_number里去,然后在下单的时候按照次序rpop出来。这样就是下一个单,然后取出来,依次进入队列,等goods_number的值为0时,终止下单。
好了,接下来,我们来看看代码是如何实现的

<?phpnamespaceHomeController;useThinkCacheDriverRedis;useThinkController;classTestControllerextendsController{publicfunctionindex(){$wheres=array();$wheres['goods_id']=1;$number=Store::where($wheres)->get(['number']);$redis=newRedis();for($i=0;$i<$number;$i++){$redis->lpush('goods_number',1);}echo$redis->llen('goods_number');}//生成唯一订单号functionbuild_order_no(){returndate('ymd').substr(implode(NULL,array_map('ord',str_split(substr(uniqid(),7,13),1))),0,8);}//记录日志functioninsertLog($event,$type=0){$data['event']=$event;$data['type']=$type;$res=M('log')->add($data);}//模拟下单操作//下单前判断redis队列库存量functionorder(){$sku_id=11;//假如传入已知的sku_id;$wheres=array();$wheres['sku_id']=$sku_id;$good_info=Store::where($wheres)->find();$user_id=rand(1,200);$goods_id=$good_info['goods_id'];$price=$good_info['price'];$number=1;//抢购时每次买一件商品$redis=newRedis();$count=$redis->rpop('goods_number');//下单时做rpop从goods_number中取出1if($count==0){$this->insertLog('error:nogoods_numberredis');return;}if(($good_info['number']-$number)<=0){$this->insertLog('商品售罄');//如果库存为0写入日志并停止下单操作return;}//生成订单$order_sn=$this->build_order_no();$data=[];$data['order_sn']=$order_sn;$data['user_id']=$user_id;$data['goods_id']=$goods_id;$data['sku_id']=$sku_id;$data['number']=$number;$data['price']=$price;$order_rs=Order::create($data);//库存减少$wheres['sku_id']=$sku_id;$store_rs=Store::where($wheres)->setDec('number',$number);if($store_rs){$this->insertLog('库存减少成功');}else{$this->insertLog('库存减少失败');}}}


1、将库存循环存入good_number中 调用index方法

商品超卖问题,你用PHP如何解决?


2.然后进行并发下单操作。这里用Apache 的ab测试
运行: 
在Windows系统下,打开cmd命令行窗口,定位到apache安装目录的bin目录下 E:phpStudyinpachepache2.4.9in
键入命令: 
ab -n 800 -c 800 http://www.startphp.cn/test/order
(-n发出800个请求,-c模拟800并发,请求数要大于或等于并发数。相当800人同时访问,后面是测试url )

商品超卖问题,你用PHP如何解决?


执行成功 查看表数据

商品超卖问题,你用PHP如何解决?
商品超卖问题,你用PHP如何解决?


商品超卖问题,你用PHP如何解决?


从表里我们可以看得出来,库存减少到0时,订单也就100个,而log表里也会清晰的记录着。

  • 发表于 2020-02-14 13:51
  • 阅读 ( 495 )
  • 分类:PHP开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1312 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章