• PHP防止订单超卖,秒杀,限购,PHP高并发防止超卖代码实践


    建表

    1.订单表

    CREATE TABLE `order` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `order_sn` varchar(45) NOT NULL DEFAULT '0' COMMENT '订单编号',
      `goods_id` int(11) NOT NULL DEFAULT '0' COMMENT '商品id',
      `uid` int(11) NOT NULL DEFAULT '0' COMMENT '用户id',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='订单表';
    

    2.库存表

    CREATE TABLE `stock` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `goods_id` int(11) NOT NULL DEFAULT '0' COMMENT '商品id',
      `num` int(11) NOT NULL DEFAULT '0' COMMENT '剩余库存',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='库存';
    

    在库存表插入一条数据id=1, goods_id=1,num=10

    测试超卖

    使用PHP的laravel框架测试

    1.PHP代码

        public function index()
        {
            //模拟接受到的数据
            $params = [
                'goods_id' => 1,//商品id
                'uid' => mt_rand(1, 1000),//随机用户uid
                'num' => 1,//购买的数量
            ];
            //查库存
            $stockInfo = DB::table('stock')->where('goods_id', $params['goods_id'])->first('num');
            $remain = $stockInfo->num - $params['num'];
            if ($remain >= 0) {
                $data = [
                    'order_sn' => date('YmdHis') . mt_rand(1000, 9999),
                    'goods_id' => $params['goods_id'],
                    'uid' => $params['uid'],
                ];
                //插入订单
                DB::table('order')->insert($data);
                //更新库存--num字段是无符号类型
                DB::table('stock')->where('goods_id', $params['goods_id'])->decrement('num', $params['num']);
                return '抢到了';
            } else {
                return '暂无库存';
            }
        }
    

    2.使用ab压测(ab压测详细使用https://www.cnblogs.com/wangzhaobo/p/8296298.html)

    ab -c 100 -n 100 "http://l8.com/"
    

    如果没有出现超卖,就再测试一下,一般3次之内就会出现超卖

    防止超卖方法

    1.使用mysql排它锁

    //使用mysql排它锁防止超卖
        public function index2()
        {
            //模拟接受到的数据
            $params = [
                'goods_id' => 1,//商品id
                'uid' => mt_rand(1,1000),//随机用户uid
                'num' => 1,//购买的数量
            ];
            //开始事物
            DB::beginTransaction();
            //查库存
            $stockInfo = DB::table('stock')->where('goods_id',$params['goods_id'])->lockForUpdate()->first('num');
            $remain = $stockInfo->num-$params['num'];
            if($remain>=0){
                $data = [
                    'order_sn' => date('YmdHis').mt_rand(1000,9999),
                    'goods_id' => $params['goods_id'],
                    'uid' => $params['uid'],
                ];
                //插入订单
                DB::table('order')->insert($data);
                //更新库存
                DB::table('stock')->where('goods_id',$params['goods_id'])->decrement('num',$params['num']);
                //提交事务
                DB::commit();
                return '抢到了';
            }else{
                //回滚事务
                DB::rollBack();
                return '暂无库存';
            }
        }
    

    使用ab测试多次,没问题

    ab -c 100 -n 100 "http://l8.com/2"
    

    2.使用redis队列

    先访问设置库存,再下单

    //设置库存
        public function index4s()
        {
            //模拟接受到的数据
            $params = [
                'goods_id' => 1,//商品id
                'uid' => mt_rand(1,1000),//随机用户uid
                'num' => 1,//购买的数量
            ];
            $rdKey = 'goods_id_'.$params['goods_id'];
            $num = DB::table('stock')->where('goods_id',$params['goods_id'])->first('num')->num;
            for($i=0;$i<$num;$i++){
                Redis::lpush($rdKey,1);
            }
            return '设置库存成功'.$num;
        }
    
        //使用redis防止超卖
        public function index4()
        {
            //模拟接受到的数据
            $params = [
                'goods_id' => 1,//商品id
                'uid' => mt_rand(1,1000),//随机用户uid
                'num' => 1,//购买的数量
            ];
            $rdKey = 'goods_id_'.$params['goods_id'];
    
            $num = Redis::lpop($rdKey);
            if($num!=1){
                return '没货了';
            }else{
                $data = [
                    'order_sn' => date('YmdHis').mt_rand(1000,9999),
                    'goods_id' => $params['goods_id'],
                    'uid' => $params['uid'],
                ];
                //插入订单
                DB::table('order')->insert($data);
                return '抢到了';
            }
        }
    

    先访问,index4s ,

    ab -c 1 -n 1 "http://l8.com/4s"
    

    后使用ab测试多次,没问题

    ab -c 100 -n 100 "http://l8.com/4"
    

    3.使用异步队列

    就是把下单扔到队列里面,前端轮询,用户排队等待结果

    代码比较简陋,记得写日志哦!

    ab压测详细使用

    https://www.cnblogs.com/wangzhaobo/p/8296298.html

  • 相关阅读:
    在iphone上安装多个微信 【微信营销必备】
    微信 5.3 for iPhone已放出 微信iphone版更新下载
    支付宝也要上"服务号"?斗战微信继续升级
    微信事业群WXG成立 致力于打造微信大平台
    Matlab绘图系列之高级绘图
    朱亚东 临睡前发点鸡汤^^
    邢波老师致广大学员的一封信(2010-10-26)
    那些逐渐消失的技艺
    Nine Great Books about Information Visualization
    linux在shell中获取时间
  • 原文地址:https://www.cnblogs.com/wangzhaobo/p/14415164.html
Copyright © 2020-2023  润新知