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 相关文章推荐
用Socket发送电子邮件
Oct 09 PHP
PHP 的 __FILE__ 常量
Jan 15 PHP
php mssql 时间格式问题
Jan 13 PHP
PHP设计模式之观察者模式(Observer)详细介绍和代码实例
Apr 08 PHP
PHP图片处理之使用imagecopyresampled函数裁剪图片例子
Nov 19 PHP
PHP模拟QQ登录的方法
Jul 29 PHP
PHP实现批量上传单个文件
Dec 29 PHP
PHP5.5迭代生成器用法实例详解
Mar 16 PHP
php处理json格式数据经典案例总结
May 19 PHP
Yii2框架RESTful API 格式化响应,授权认证和速率限制三部分详解
Nov 10 PHP
php操作mongodb封装类与用法实例
Sep 01 PHP
详解php用static方法的原因
Sep 12 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
《OVERLORD》手游英文版即将上线 手机上也能扮演骨王
2020/04/09 日漫
php开发文档 会员收费1期
2012/08/14 PHP
PHP连接Access数据库的方法小结
2013/06/20 PHP
php+mysql查询优化简单实例
2015/01/13 PHP
表单提交错误后返回内容消失问题的解决方法(PHP网站)
2015/10/20 PHP
详解PHP中的 input属性(隐藏 只读 限制)
2017/08/14 PHP
JavaScript加密解密7种方法总结分析
2007/10/07 Javascript
javascript 判断数组是否已包含了某个元素的函数
2010/05/30 Javascript
Jquery实现图片左右自动滚动示例
2013/09/25 Javascript
Nodejs极简入门教程(三):进程
2014/10/27 NodeJs
基于Bootstrap的后台管理面板 Bootstrap Metro Dashboard
2016/06/17 Javascript
AngularJS实现标签页的两种方式
2016/09/05 Javascript
WEB 前端开发中防治重复提交的实现方法
2016/10/26 Javascript
jQuery动态产生select option下拉列表
2017/03/15 Javascript
详解Node.js利用node-git-server快速搭建git服务器
2017/09/27 Javascript
vue 子组件和父组件传值的示例
2020/09/11 Javascript
微信小程序实现点击导航标签滚动定位到对应位置
2020/11/19 Javascript
原生js实现自定义消息提示框
2020/11/19 Javascript
[14:36]2014 DOTA2国际邀请赛中国区预选赛5.21 Orenda VS NE
2014/05/22 DOTA
Python中正则表达式的详细教程
2015/04/30 Python
Python按行读取文件的实现方法【小文件和大文件读取】
2016/09/19 Python
djang常用查询SQL语句的使用代码
2019/02/15 Python
python多线程分块读取文件
2019/08/29 Python
浅谈pycharm使用及设置方法
2019/09/09 Python
keras 简单 lstm实例(基于one-hot编码)
2020/07/02 Python
css3 仿写阿里云水纹效果的示例代码
2018/02/10 HTML / CSS
HTML5 3D旋转相册的实现示例
2019/12/03 HTML / CSS
英国时尚家具、家居饰品及礼品商店:Graham & Green
2016/09/15 全球购物
自荐信如何“自荐”
2013/10/24 职场文书
五心教育心得体会
2014/09/04 职场文书
2014最新预备党员思想汇报范文:中国梦,我的梦
2014/10/25 职场文书
2014年公务员个人工作总结
2014/11/22 职场文书
运动会开幕式致辞
2015/07/29 职场文书
五一放假通知怎么写
2015/08/18 职场文书
Python实现生活常识解答机器人
2021/06/28 Python
HTTP中的Content-type详解
2022/01/18 HTML / CSS