zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    代理模式以及operator>()的重载
    asp.net 2.0中gridview里嵌套dropdownlist
    .Net的编码规范
    Google GMail使用技巧
    推荐一些我经常参考的ASP.NET2.0的学习网站
    petShop 4.0 的命名空间 以及各个项目模块的说明
    超强口误
    当每次鼠标点选GRIDVIEW每行的文本框时,该行会加亮
    ASP.NET2.0中Gridview中数据操作技巧
    ASP.NET中的DataGrid控件示例
  • 原文地址:https://www.cnblogs.com/wangzhaobo/p/14415164.html
Copyright © 2011-2022 走看看