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 相关文章推荐
PHP5.0正式发布 不完全兼容PHP4 新增多项功能
Oct 09 PHP
使用 MySQL Date/Time 类型
Mar 26 PHP
隐性调用php程序的方法
Mar 09 PHP
解析PHP中如何将数组变量写入文件
Jun 06 PHP
php数组添加与删除单元的常用函数实例分析
Feb 16 PHP
php截取指定2个字符之间字符串的方法
Apr 15 PHP
PHP 断点续传实例详解
Nov 11 PHP
PHP实现绘制二叉树图形显示功能详解【包括二叉搜索树、平衡树及红黑树】
Nov 16 PHP
thinkPHP5框架导出Excel文件简单操作示例
Aug 03 PHP
PHP实现的用户注册表单验证功能简单示例
Feb 25 PHP
tp5框架的增删改查操作示例
Oct 31 PHP
PHP基于phpqrcode类生成二维码的方法示例详解
Aug 07 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新手上路(五)
2006/10/09 PHP
php验证手机号码
2015/11/11 PHP
php批量修改表结构实例
2017/05/24 PHP
ThinkPHP框架实现的邮箱激活功能示例
2018/06/15 PHP
Draggable Elements 元素拖拽功能实现代码
2011/03/30 Javascript
javascript基础知识大全 便于大家学习,也便于我自己查看
2012/08/17 Javascript
jquery next nextAll nextUntil siblings的区别介绍
2013/10/05 Javascript
JS对HTML标签select的获取、添加、删除操作
2013/10/17 Javascript
div浮层,滚动条移动,位置保持不变的4种方法汇总
2013/12/11 Javascript
node.js中的require使用详解
2014/12/15 Javascript
JavaSacript中charCodeAt()方法的使用详解
2015/06/05 Javascript
Java中Timer的用法详解
2015/10/21 Javascript
Bootstrap多级导航栏(级联导航)的实现代码
2016/03/08 Javascript
用v-html解决Vue.js渲染中html标签不被解析的问题
2016/12/14 Javascript
原生js实现可拖动的登录框效果
2017/01/21 Javascript
jQuery插件FusionCharts绘制的3D双柱状图效果示例【附demo源码】
2017/04/20 jQuery
简单谈谈原生js的math对象
2017/06/27 Javascript
详解vue中computed 和 watch的异同
2017/06/30 Javascript
实例介绍JavaScript中多种组合继承
2019/01/20 Javascript
使用vue-router切换页面时实现设置过渡动画
2019/10/31 Javascript
Python开发之快速搭建自动回复微信公众号功能
2016/04/22 Python
Python控制多进程与多线程并发数总结
2016/10/26 Python
浅析python实现scrapy定时执行爬虫
2018/03/04 Python
jupyter notebook的安装与使用详解
2020/05/18 Python
实现Python3数组旋转的3种算法实例
2020/09/16 Python
如何用Python 实现全连接神经网络(Multi-layer Perceptron)
2020/10/15 Python
html5实现九宫格抽奖可固定抽中某项奖品
2020/06/15 HTML / CSS
Guess欧洲官网:美国服饰品牌
2019/08/06 全球购物
先进教育工作者事迹材料
2014/12/23 职场文书
毕业设计致谢语
2015/05/14 职场文书
观看安全警示教育片心得体会
2016/01/15 职场文书
遇事可以测出您的见识与格局
2019/09/16 职场文书
浅谈Python列表嵌套字典转化的问题
2021/04/07 Python
CSS3 制作的书本翻页特效
2021/04/13 HTML / CSS
css height属性中的calc方法详解
2021/06/03 HTML / CSS
idea搭建可运行Servlet的Web项目
2021/06/26 Java/Android