并发下超发问题解决

解决方案:乐观锁,悲观锁,redis watch,redis分布式锁,消息队列

  • 将库存字段number字段设为unsigned,当库存为0时,因为字段不能为负数,将会返回false

乐观锁

  //乐观锁,通过版本号实现,数据库中加入一个version,A用户读取version等于1=时,完成减扣之后将version+1
    DB::connection("mysql_member")->beginTransaction();
    $version = 1;   //假设A用户拿到的版本是1,根据实际用户获取到版本号
    $goods_id = 1000;   //商品id
    try {
        //判断数据
        $result = LotteryPrizeType::query()->where('version', '=', $version)->where('goods_id', '=', $goods_id)->first();
        if (!$result) {   
           throw new ApiException("未找到数据");
        }
        //先判断库存
        $result = LotteryPrizeType::query()->where('goods_id', '=', $goods_id)->first();
        if ($result['num'] <= 0) {    //判断库存
            DB::connection("mysql_member")->rollBack();
            throw new ApiException("库存不足");
        }
        LotteryPrizeType::query()->where('version', '=', $version)->increment("version");   //增加版本号
        LotteryPrizeType::query()->where('goods_id', '=', $version)->decrement("num");  //减去库存

        DB::connection("mysql_member")->commit();
    } catch (\Exception $exception) {
        DB::connection("mysql_member")->rollBack();
        throw new ApiException($exception->getMessage());
    }

悲观锁

//悲观锁,也就是在修改数据的时候,采用锁定状态,排斥外部请求的修改。遇到加锁的状态,就必须等待。
    DB::connection("mysql_member")->beginTransaction();
    $version = 1;   //假设A用户拿到的版本是1,根据实际用户获取到版本号
    $goods_id = 1000;   //商品id
    try {
        $result = LotteryPrizeType::query()->where('goods_id', '=', $goods_id)->lockForUpdate()->first();
        if ($result['num'] <= 0) {    //判断库存
            DB::connection("mysql_member")->rollBack();
            throw new ApiException("库存不足");
        }
        LotteryPrizeType::query()->where('goods_id', '=', $version)->decrement("num");  //减去库存

        DB::connection("mysql_member")->commit();
    } catch (\Exception $exception) {
        DB::connection("mysql_member")->rollBack();
        throw new ApiException($exception->getMessage());
    }

redis的watch

//1.比如我有商品详情页详情接口将库存写入到redis
    //2.通过watch从redis判断库存是否剩余

    $goodsId = 10000;   //商品id
    $lottery = LotteryPrizeType::query()->where('goods_id', $goodsId)->first();
    if (empty($lottery)) {
        throw new ApiException("未找到数据");
    }
    $key = "goods_id" . "-" . $goodsId;
    $redis = Redis::connection();
    $redis->select(0);  //选择数据库
    $number = 10;  //库存数据
    $n = $this->redis->get($key) ?: 0;  //获取redis的库存
    if ($n > 0) {
        $this->redis->watch($key);
        $this->redis->multi();
        $this->redis->incr($key);
        $exec = $this->redis->exec();
        if ($exec) {
            //减扣成功
        }
    }else{
        //库存不足
    }

redis分布式锁

posted @ 2021-08-20 09:40  惊风破浪的博客  阅读(64)  评论(0编辑  收藏  举报