• 避免商品超卖的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")
         }
      }
    }
  • 相关阅读:
    【转】 IntelliJ IDEA 详细图解最常用的配置 ,适合刚刚用的新人
    安装IntelliJ IDEA默认C盘文件过大怎么办
    [linux]netstat命令详解-显示linux中各种网络相关信息
    [linux]free命令详解-显示内存的使用情况
    用什么工具能找出性能瓶颈?
    [linux]iostat命令详解-监视系统输入输出设备和CPU的使用情况
    [linux]vmstat命令详解-显示虚拟内存状态
    好文章列表
    BigDecimal.setScale 处理java小数点
    Mybatis通用分页
  • 原文地址:https://www.cnblogs.com/dawuge/p/10480469.html
Copyright © 2020-2023  润新知