前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >推荐10-避免商品超卖的4种方案

推荐10-避免商品超卖的4种方案

作者头像
猿哥
发布2019-09-19 17:18:20
1.1K0
发布2019-09-19 17:18:20
举报
文章被收录于专栏:Web技术布道师Web技术布道师

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

代码语言:javascript
复制
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获取不到连接而报错,或者是超过等待时间而报错

代码语言:javascript
复制
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

代码语言:javascript
复制
public function index()
{
  //测试并发超卖现象
  if (Redis::setnx(self::KEY, 1)) {//拿到了锁
    $this->buy();
  } else {
    usleep(100000);//等会再去拿锁
    //Log::info("未争夺到锁,睡眠100ms");
    $this->index();
  }
}
代码语言:javascript
复制
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, 无丢失, 无框架)

代码语言:javascript
复制
public function push()
{
  //入队列
  Redis::lpush(self::QUEUE, 1);
}

//脚本调用pop方法 * * * * * php xxx.php

代码语言:javascript
复制
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")
     }
  }

}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PHP技术大全 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档