zoukankan      html  css  js  c++  java
  • 高并发超卖问题:测试出现超卖问题和解决方案

    说明:当前测试为thinkphp5环境下的代码、不考虑用户uid问题,只考虑库存问题

    准备:

      1. 新建两个表(goods、orders)

    CREATE TABLE `goods` (
    `id` int NOT NULL AUTO_INCREMENT,
    `name` varchar(30) NOT NULL DEFAULT '',
    `number` int NOT NULL DEFAULT '0',
    `price` int NOT NULL DEFAULT '1',
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=gbk
    
    CREATE TABLE `order` (
    `id` int NOT NULL AUTO_INCREMENT,
    `uid` int NOT NULL DEFAULT '0',
    `goods_id` int NOT NULL DEFAULT '0',
    `number` int NOT NULL DEFAULT '0',
    `add_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=gbk
    

      2.插入两条商品数据

    insert into `seo`.`ctx_goods` ( `name`, `number`, `price`) values ( '苹果手机', '10', '1');
    insert into `seo`.`ctx_goods` ( `name`, `number`, `price`) values ( '苹果电脑', '3', '2');
    

      3.购买商品业务代码

    public function order(){
    
        $goods_id = 2;
        $number = 1;
        // 启动事务
        Db::startTrans();
        try{
            $goods = Db::name('goods')->lock(true)->find($goods_id);
            if($goods['number']>0){
                $res1 = Db::name('goods')->where(['id' => $goods_id])->setDec('number', $number);
                $orderData = [
                    'goods_id' => $goods['id'],
                    'uid' => 1,
                    'number' => $number,
                ];
                $res2 = Db::name('order')->insert($orderData);
                if($res1 !==false && $res2){
                    // 提交事务
                    Db::commit();    
                    exit('ok');
                }else{
                    throw new Exception(' 下单失败');
                }
            }else{
                throw new Exception(' 库存不足');
            }
        } catch (Exception $e) {
            // 回滚事务
            Db::rollback();
            exit('error');
        }
        exit;
    }
    

      4. 测试产生超卖问题

      注意:ab测试工具如果没有安装请百度搜索ab工具安装

    ab -c500 -n800 -k http://seo_ctx.cn/portal/index/order

      5. 结果

      商品表数据

        

      订单表数据

        

       

      出现问题原因:

      高并发,导致mysql查询同出来的数据一样,而在同一时间修改没有立即生效,之后这些修改都会被执行。

      从而出现,库存只有3个却生成了3个以上的订单

      解决方案:

      1.设置数据库行锁

      使用行锁,限制同一时间只能进行一个数据修改操作

    将
    $goods = Db::name('goods')->find($goods_id);
    改成
    $goods = Db::name('goods')->lock(true)->find($goods_id);
    

      2.使用redis

      用redis去维护库存

    • redis是基于内存的,内存的读写速度非常快;
    • redis是单线程的,省去了很多上下文切换线程的时间;
    public function order(){
        $goods_id = 2;
        $number = 1;
        $goods = Db::name('goods')->find($goods_id);
    
        $redis = new Redis();
        $redis->connect('127.0.0.1', 6379);
        if($redis->get('goodsNumber:'.$goods_id)!==false){
            //设置初始库存
            $redis->set('goodsNumber:'.$goods_id, $goods['number']);
        }
        // 启动事务
        Db::startTrans();
        try{
            $remain_number = $redis->get('goodsNumber:'.$goods_id);
            if($remain_number>0){
                $redis->decr('goodsNumber:'.$goods_id, 1);
                $res1 = Db::name('goods')->where(['id' => $goods_id])->setDec('number', $number);
                $orderData = [
                    'goods_id' => $goods_id,
                    'uid' => 1,
                    'number' => $number,
                ];
                $res2 = Db::name('order')->insert($orderData);
    
                
                if($res1 !==false && $res2){
                    // 提交事务
                    Db::commit();
                    exit('ok');
                }else{
                    throw new Exception(' 下单失败');
                }
            }else{
                throw new Exception(' 库存不足');
            }
        } catch (Exception $e) {
            // 回滚事务
            Db::rollback();
            exit('error');
        }
        exit;
    }
    

      

      

    小绵羊
  • 相关阅读:
    iOS设计模式:观察者
    Java面向接口编程小例子
    《The DeadLine》(《最后期限》) 读后感
    Codeforces Round #395 Div1的A题Timofey and a tree
    重写和强制转换再调用能编译但不能运行
    Java继承和静态-加载顺序
    C++之pair
    用Java面向对象思想实现一个微博的功能(未完)
    Java对象在内存图示
    Java中OOP对象和引用
  • 原文地址:https://www.cnblogs.com/lauhp/p/14431949.html
Copyright © 2011-2022 走看看