避免商品超卖的4种方案

18 篇文章 0 订阅
17 篇文章 0 订阅

避免商品超卖的4种方案(以下是一些核心思路)

原始方案(失败):在每次下订单前我们判断促销商品的数量够不够,不够不允许下订单,更改库存量时加上一个条件,只更改商品库存大于0的商品的库存,当时我们使用ab进行压力测试,当并发超过500,访问量超过2000时,还是会出现超卖现象。

public function buyOne() {
    $shop = Shop::find(1);
    if ($shop->number > 0) {
        DB::update("update shop set number = number - 1 where id = 1");
    }
}

第1种方案:使用mysql的事务加排他锁来解决,首先我们选择数据库的存储引擎为innoDB,使用的是排他锁实现的,刚开始的时候我们测试了下共享锁,发现还是会出现超卖的现象。有个问题是,当我们进行高并发测试时,对数据库的性能影响很大,导致数据库的压力很大。

//2.利用数据库的forupdate来加锁(在数量少的情况下并不会出现问题,但是当并发达到(ab -n 1000 -c 200),
//就会出现请求非2XX的响应增多,1000 失败了 60)time per request 65.195
//在高并发的情况下,会导致数据库连接数不够,部分php获取不到连接而报错,或者是超过等待时间而报错
public function indexMysql() {
    DB::beginTransaction();
    //通过for update 加排它锁
    $shop = DB::table('shop')->where('id', '=', 1)->lockForUpdate()->first();
    if ($shop->number > 0) {
        if (DB::update("update shop set number = number - 1 where id = 1")) {
            DB::commit();
        } else {
            DB::rollBack();//回滚并重试
            usleep(100000);
            $this->indexMysql();
        }
    } else {
        DB::commit();
    }
}

第2种方案:使用文件锁实现。当用户抢到一件促销商品后先触发文件锁,防止其他用户进入,该用户抢到促销品后再解开文件锁,放其他用户进行操作。这样可以解决超卖的问题,但是会导致文件得I/O开销很大。

第3种方案:使用redis的setnx来实现锁机制。但是并发大的情况下,锁的争夺会变多,导致响应越来越慢。(与第四种方案类似)

//在数量少的情况下并不会出现问题,但是当并发达到(ab -n 1000 -c 200 就会出现请求非2XX的响应增多,1000 失败了 54) time per request 127.575
public function index() {
    //测试并发超卖现象
    if (Redis::setnx(self::KEY, 1)) {//拿到了锁
        $this->buy();
    } else {
        usleep(100000);//等会再去拿锁
        //Log::info("未争夺到锁,睡眠100ms");
        $this->index();
    }
}

private function buy() {
    $shop = Shop::find(1);
    if ($shop->number > 0) {
        $shop->number --;
        $shop->save();
    }
    Redis::del(self::KEY);
}

第4种方案:redis的队列来实现。将要促销的商品数量以队列的方式存入redis中,每当用户抢到一件促销商品则从队列中删除一个数据,确保商品不会超卖。这个操作起来很方便,而且效率极高

//4.使用redis队列来,用户过来直接入队列,然后再将操作更新到数据库
//最佳体验(redis pconnect 9.481s, 无丢失, 无框架)
public function push() {
    //入队列
    Redis::lpush(self::QUEUE, 1);
}

//脚本调用pop方法 * * * * * php xxx.php
public function pop() {
    while (($key = Redis::rpop(self::QUEUE))) {
        $shop = Shop::find(1);
        if ($shop->number > 0) {
            DB::update("update shop set number = number - 1 where id = 1")
        }
    }
}
在Redisson+RabbitMQ Java微服务项目家具商城中,我们可以通过以下方式解决商品超卖问题、消息丢失问题、订单重复提交问题高并发问题: 1. 商品超卖问题的解决方案: 在家具商城中,商品超卖是一常见的问题。为了解决这个问题,我们可以使用 Redisson 的分布式锁机制来避免多个用户同时购买同一件商品的情况。 当用户开始购买商品时,我们可以使用 Redisson 的分布式锁来锁定该商品。这样,其他用户就不能同时购买该商品。当用户购买完成时,我们可以释放该商品的锁。 2. 消息丢失问题的解决方案: 在家具商城中,消息丢失是另一常见的问题。为了解决这个问题,我们可以使用 RabbitMQ 的消息确认机制。当消息被发送到队列中时,我们可以设置消息确认机制,确保消息被正确地接收并处理。 3. 订单重复提交问题的解决方案: 在家具商城中,订单重复提交也是一常见的问题。为了解决这个问题,我们可以使用 Redisson 的分布式锁机制来避免用户重复提交订单。 当用户开始提交订单时,我们可以使用 Redisson 的分布式锁来锁定订单。这样,其他用户就不能同时提交该订单。当用户提交订单成功时,我们可以释放该订单的锁。 4. 高并发问题的解决方案: 在家具商城中,高并发是一个非常常见的问题。为了解决这个问题,我们可以使用 Redisson 的分布式锁机制来保证同一时间只有一个用户可以访问某个资源,从而避免资源的并发访问。 此外,我们还可以使用 RabbitMQ 的消息队列来实现异步处理,避免因为高并发访问而导致系统崩溃。 总之,通过使用 Redisson+RabbitMQ Java微服务项目家具商城中的上述解决方案,我们可以有效地解决商品超卖问题、消息丢失问题、订单重复提交问题高并发问题
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值