php处理抢购类功能的高并发请求


Posted in PHP onFebruary 08, 2018

本文以抢购、秒杀为例。介绍如何在高并发状况下确保数据正确。
在高并发请求下容易参数两个问题
1.数据出错,导致产品超卖。
2.频繁操作数据库,导致性能下降。

测试环境

Windows7
apache2.4.9
php5.5.12
php框架 yii2.0
工具 apache bench (apache自带高并发请求工具)。

通常处理方法

从控制器可以看出代码思路。先查询商品库存。如果库存大于0 ,则库存减少1,同时生产订单,录入抢购者数据。

// 常规代码处理高并发
  public function actionNormal(){
    // 查询库存
    $stock = Goods::find()->select('stock')->where(['goods_id'=>100001])->asArray()->one();
    // 判断该商品是否还有库存
    if ($stock['stock']>0) {
      // 库存减一
      Goods::updateAllCounters(['stock' => -1],['goods_id'=>100001]);

      // 生产订单(另外功能,暂且随机赋值)
      $order = $this->build_order();

      // 秒杀信息入库
      $model = new Highly();
      $model->order_id = $order;
      $model->goods_name = '秒杀商品';
      $model->buy_time = date('Y-m-d H:i:s',time());
      $model->mircrotime = microtime(true);
      if($model->save()===false){
        echo '未能成功抢购!';
      }else{
        echo '恭喜你,订单<b>'.$order.'</b>抢购成功';
      }

    }else{
      echo '已被抢购一空!';
    }
  }

将商品库存设置为20后,通过ab 配置200的并发请求。

ab -n 200 -c 200 http//localhost/highly/normal

执行结果发现库存变成了负值,商品超卖了。

php处理抢购类功能的高并发请求

原因比较简单,在高并发请求下。在生产订单,减少库存之前,会优先查询到库存结果。

优化一:修改库存数据类型

第一种优化方法,从数据库入手。既然查询到的结果不准确,那我就在库存减少上做手脚。将库存的数据类型改成无符号(不能有负值)。

代码还是跟上面差不多,只是在库存减1的地方做了个判断。避免报错。

public function actionNormal(){
    // 查询库存
    $stock = Goods::find()->select('stock')->where(['goods_id'=>100001])->asArray()->one();
    // 判断该商品是否还有库存
    if ($stock['stock']>0) {
      // 库存减一
      if(Goods::updateAllCounters(['stock' => -1],['goods_id'=>100001])===false){
        echo "已被抢购一空!";
        return false;
      }

      // 生产订单(另外功能,暂且随机赋值)
      $order = $this->build_order();

      // 秒杀信息入库
      $model = new Highly();
      $model->order_id = $order;
      $model->goods_name = '秒杀商品';
      $model->buy_time = date('Y-m-d H:i:s',time());
      $model->mircrotime = microtime(true);
      if($model->save()===false){
        echo '未能成功抢购!';
      }else{
        echo '恭喜你,订单<b>'.$order.'</b>抢购成功';
      }

    }else{
      echo '已被抢购一空!';
    }
  }

这一次同样200的并发,执行结果发现。数据正确,并不会出现超卖的情况。
思路其实也比较简单。因为库存不能为负值,当库存等于0时,如果还有值传进来,则会报错。请求被终止。

这种优化方式,虽然避免了商品超卖的情况。但是在另一方面,请求仍然会对数据库造成压力。如果多个功能使用此数据库,会造成性能下降厉害。

优化二:redis

利用 redis list类型的pop的原子性。在操作数据库前,做一个验证。当商品卖完后,就不允许再继续进行数据库操作。

// redis list 高并发测试
  public function actionRedis(){
    $redis = \Yii::$app->redis;
    // $redis->lpush('mytest',1);
    $order = $this->build_order();
    // echo $order;die;
    // echo $redis->llen('mytest');
    $reg = $redis->lpop('mytest');
    if (!$reg) {
      echo "笨蛋!已经被抢光啦!";
      return false;
    }
    $redis->close();
    $model = new Highly();
    $model->order_id = $order;
    $model->goods_name = '秒杀商品';
    $model->buy_time = date('Y-m-d H:i:s',time());
    $model->mircrotime = microtime(true);

    if($model->save()===false){
      echo '未能成功抢购!';
    }else{
      echo '恭喜你,订单<b>'.$order.'</b>抢购成功';
    }
  }
  // 给redis添加商品
  public function actionInsertgoods(){
    $count = yii::$app->request->get('count',0);
    if (empty($count)) {
      echo '大兄弟,你还没告诉我需要上架多少商品呢!';
      return false;
    }
    $redis = \Yii::$app->redis;
    for ($i=0; $i < $count; $i++) { 
      $redis->lpush('mytest',1);
    }
    echo '成功添加了'.$redis->llen('mytest').'件商品。';
    $redis->close();

  }

这点的代码,我写了两个方法。第一个方法是秒杀的代码,第二个方法是给秒杀的商品设置数量。为了方便测试,我这里处理的比较简单。

通过测试,数据库生产的订单数量正常,并没有出现问题。而又避免了请求数据库造成性能下降的问题。同时内存数据库redis查询的速度要比mysql快很多。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
php中变量及部分适用方法
Mar 27 PHP
PHP 采集心得技巧
May 15 PHP
让PHP开发者事半功倍的十大技巧小结
Apr 20 PHP
php防攻击代码升级版
Dec 29 PHP
php 5.3.5安装memcache注意事项小结
Apr 12 PHP
解析php dirname()与__FILE__常量的应用
Jun 24 PHP
PHP中的类型提示(type hinting)功能介绍
Jul 01 PHP
Laravel使用Caching缓存数据减轻数据库查询压力的方法
Mar 15 PHP
php断点续传之文件分割合并详解
Dec 13 PHP
PHP从数组中删除元素的四种方法实例
May 12 PHP
php中请求url的五种方法总结
Jul 13 PHP
Laravel 解决composer相关操作提示php相关异常的问题
Oct 23 PHP
php+redis实现商城秒杀功能
Nov 19 #PHP
php+redis消息队列实现抢购功能
Feb 08 #PHP
PHP多线程模拟实现秒杀抢单
Feb 07 #PHP
PHP设计模式之装饰器模式实例详解
Feb 07 #PHP
PHP使用星号替代用户名手机和邮箱的实现代码
Feb 07 #PHP
PHP unlink与rmdir删除目录及目录下所有文件实例代码
Feb 07 #PHP
php删除一个路径下的所有文件夹和文件的方法
Feb 07 #PHP
You might like
一个不易被发现的PHP后门代码解析
2014/07/05 PHP
实例化php类时传参的方法分析
2020/06/05 PHP
Windows Live的@live.com域名注册漏洞 利用代码
2006/12/27 Javascript
把html页面的部分内容保存成新的html文件的jquery代码
2009/11/12 Javascript
IE8 chrome中table隔行换色解决办法
2010/07/09 Javascript
html中table数据排序的js代码
2011/08/09 Javascript
javascript 使td内容不换行不撑开
2012/11/29 Javascript
JavaScript获取/更改文本框的值的实例代码
2013/08/02 Javascript
页面载入结束自动调用js函数示例
2013/09/23 Javascript
Jsonp 关键字详解及json和jsonp的区别,ajax和jsonp的区别
2015/12/30 Javascript
Node.js静态文件服务器改进版
2016/01/10 Javascript
Javascript获取随机数的实现方法
2016/06/22 Javascript
Node.js DES加密的简单实现
2016/07/07 Javascript
AngularJs expression详解及简单示例
2016/09/01 Javascript
Jquery获取radio选中的值
2017/05/05 jQuery
关于Vue Webpack2单元测试示例详解
2017/08/14 Javascript
原生JS实现多个小球碰撞反弹效果示例
2018/01/31 Javascript
vue组件通信传值操作示例
2019/01/08 Javascript
python 从远程服务器下载东西的代码
2013/02/10 Python
Python自定义scrapy中间模块避免重复采集的方法
2015/04/07 Python
讲解Python中运算符使用时的优先级
2015/05/14 Python
Python wxPython库消息对话框MessageDialog用法示例
2018/09/03 Python
pytorch permute维度转换方法
2018/12/14 Python
Python3 requests文件下载 期间显示文件信息和下载进度代码实例
2019/08/16 Python
python模式 工厂模式原理及实例详解
2020/02/11 Python
在Windows上安装和配置 Jupyter Lab 作为桌面级应用程序教程
2020/04/22 Python
pycharm设置默认的UTF-8编码模式的方法详解
2020/06/01 Python
10 套华丽的CSS3 按钮小结
2012/10/03 HTML / CSS
使用phonegap进行本地存储的实现方法
2017/03/31 HTML / CSS
装修致歉信
2014/01/15 职场文书
网站创业计划书
2014/04/30 职场文书
水利局群众路线专题民主生活会发言材料
2014/09/21 职场文书
2014大学生批评与自我批评思想汇报
2014/09/21 职场文书
国防教育标语
2014/10/08 职场文书
模范班主任事迹材料
2014/12/17 职场文书
工伤认定行政答辩状
2015/05/22 职场文书