前言在商品秒杀活动中,例如商品库存只有100个,但在抢购活动中,可能有200人同时抢购,所以有并发,下单100个就清点完毕如果为0,有可能继续下单成功,出现超卖。为了解决这个问题,今天主要讲一下用redis队列进行处理。Redis有一个列表类型,它实际上是一个双向链表。通过lpush和pop操作从链表的头部或尾部添加和删除元素。这允许将列表用作堆栈和队列。先进先出,一端进一端出,这就是队列。队列中上一个走完了,下一个就走,所以redis队列可以完美的解决超卖并发的问题。闪购和超卖问题还有其他的解决方法:1、使用mysql事务加排他锁解决;2.使用文件锁来实现。3、利用redis的setnx实现锁机制等点击查看:4种避免商品超卖的解决方案实现原理lpush商品库存到num,然后下单时通过rpop一次取出1个商品。当num值为0时,停止下单。步骤1创建表一共有三张表,分别是:订单表、商品表、日志表。1.订单表CREATETABLE`ims_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'Price:Unitiscent',`create_time`timestampNOTNULLDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=5820DEFAULTCHARSET=utf8COMMENT='Ordertable'2.商品表CREATETABLE`ims_hotmallstore_goods`(`id`int(11)unsignedNOTNULLAUTO_INCREMENT,`name`varchar(50)NOTNULLCOMMENT'产品名称',`type_id`int(11)NOTNULLCOMMENT'产品类别',`img`textNOTNULLCOMMENT'ProductImage',`money`decimal(10,2)NOTNULLCOMMENT'Price',`money2`decimal(10,2)NOTNULLCOMMENT'OriginalPrice',`is_show`int(11)NOTNULLDEFAULT'1'COMMENT'1.现货2.现货',`uniacid`int(11)NOTNULLCOMMENT'appletid',`inventory`int(11)NOTNULLCOMMENT'inventory',`details`textNOTNULLCOMMENT'details',`store_id`int(11)NOTNULLCOMMENT'merchantid',`sales`int(11)NOTNULLCOMMENT'sales',`logo`varchar(100)NOTNULL,`num`int(11)NOTNULL,`is_gg`int(11)NOTNULLDEFAULT'2'COMMENT'是否启用规范',PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=2DEFAULTCHARSET=utf83.日志表CREATETABLE`ims_order_log`(`id`int(11)NOTNULLAUTO_INCREMENT,`status`int(11)NOTNULLDEFAULT'0',`msg`textCHARACTERSETutf8,`create_time`timestampNOTNULLDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(`id`),KEY`status`(`status`))ENGINE=InnoDBAUTO_INCREMENT=4562DEFAULTCHARSET=gb2312COMMENT='orderlogtable'step2编写代码classTest{privatestatic$instance=null;//单列模式Redis实例publicstaticfunctionRedis(){if(self::$instance==null){$redis=new\Redis();$redis->connect('127.0.0.1',6379);自我::$实例=$redis;}返回自我::$实例;}//将产品库存循环到lpushpubl的numic函数doPageSaveNum(){$redis=self::Redis();$goods_id=1;$sql="selectid,num,moneyfromims_hotmallstore_goodswhereid=".$goods_id;$goods=pdo_fetch($sql);if(!empty($goods)){for($i=1;$i<=$goods['num'];$i++){$redis->lpush('num',$i);}die('成功!');}else{$this->echoMsg(0,'商品不存在');}}//抢购下单publicfunctiondoPageGoodsStore(){$goods_id=1;$sql="selectid,num,moneyfromims_hotmallstore_goodswhereid=".$goods_id;$goods=pdo_fetch($sql);$redis=self::Redis();$count=$redis->rpop('num');//每次从num取出1if($count==0){$this->echoMsg(0,'nonumredis');}$this->doPageGoodsOrder($goods,1);}//保存日志publicfunctionechoMsg($status,$msg,$_data=""){$data=json_encode(array('status'=>$status,'msg'=>$msg,'data'=>$_data),JSON_UNESCAPED_UNICODE);$order_log['status']=$status;$order_log['msg']=$msg;$order_log['create_time']=time();pdo_insert('order_log',$order_log);死($数据);}publicfunctionorderNo(){returndate('ymd').substr(implode(NULL,array_map('ord',str_split(substr(uniqid(),7,13),1))),0,8);}//订单更新库存publicfunctiondoPageGoodsOrder($goods,$goods_number){$orderNo=$this->orderNo();$number=$goods['num']-$goods_number;if($number<0){$this->echoMsg(0,'没有货');}$user_id=rand(1,500);$order['user_id']=$user_id;$order['goods_id']=$goods['id'];$order['number']=$goods_number;$order['price']=$goods['money'];$订单['状态']=1;$order['sku_id']=2;$order['order_sn']=$orderNo;$order['create_time']=date('Y-m-dH:i:s');pdo_insert('订单',$订单);$sql="updateims_hotmallstore_goodssetnum=num-".$goods_number."wherenum>0andid=".$goods['id'];$res=pdo_query($sql);if(!empty($res)){$this->echoMsg(1,'库存扣除成功'.$number);$redis=self::Redis();$redis->lpush('num',$goods_number);$this->echoMsg(0,'库存扣减失败'.$numb呃);}}//call--循环产品库存到lpush的numif($_GET['i']==1){$model=newTest;$model->doPageSaveNum();}//调用--高并发急单if($_GET['i']==2){$model=newTest;$model->doPageGoodsStore();}Step3并发测试1.先手动执行:http://127.0.0.1/wqchunjingsvn/web/index.php?i=1,循环保存商品库存到lpush的num2.这里我用的是Apache的ab测试,安装方法会在文末补充。打开终端执行:ab-n1000-c200http://127.0.0.1/wqchunjingsvn/web/index.php?i=2(-n发送1000个请求,-c模拟200并发,请求数必须大于或等于并发数。相当于1000人同时访问,后面是测试url)3.观察是否执行成功。执行结果如下图所示,说明执行成功。第四步查看数据表1.查看订单表,总订单数量为100,如下图,没有问题。2.查看商品库存,从100变成0,没有问题。3、查看日志表,一共有137条记录,其中status为1的只有100条,没有问题。总结分析1、方案可行,库存为0,不存在超卖情况。2、使用Apache的ab测试高并发时,需要注意Url地址不能和带&符号的参数拼接,否则会执行失败。相关资料使用redis解决php中秒杀超限问题如何解决高并发秒杀超限问题
