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 相关文章推荐
加速XP搜索功能堪比vista
Mar 22 PHP
php cli 方式 在crotab中运行解决
Feb 08 PHP
PHP在字符断点处截断文字的实现代码
Apr 21 PHP
php对数组排序代码分享
Feb 24 PHP
php 无限级分类,超级简单的无限级分类,支持输出树状图
Jun 29 PHP
php带抄送和密件抄送的邮件发送方法
Mar 20 PHP
php实现比较两个字符串日期大小的方法
May 12 PHP
详解Window7 下开发php扩展
Dec 31 PHP
JSON两种结构之对象和数组的理解
Jul 19 PHP
PHP goto语句用法实例
Aug 06 PHP
使用tp框架和SQL语句查询数据表中的某字段包含某值
Oct 18 PHP
PHP number_format函数原理及实例解析
Jul 14 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
dedecms 制作模板中使用的全局标记图文教程
2007/03/11 PHP
PHP中Trait及其应用详解
2017/02/14 PHP
PHP中创建和编辑Excel表格的方法
2018/09/13 PHP
jquery实现仿JqueryUi可拖动的DIV实例
2015/07/31 Javascript
Jquery数字上下滚动动态切换插件
2015/08/08 Javascript
js精美的幻灯片画集特效代码分享
2015/08/29 Javascript
js的各种排序算法实现(总结)
2016/07/23 Javascript
AngularJs自定义服务之实现签名和加密
2016/08/02 Javascript
javascript函数中的3个高级技巧
2016/09/22 Javascript
IOS中safari下的select下拉菜单文字过长不换行的解决方法
2016/09/26 Javascript
jQuery ajax读取本地json文件的实例
2017/10/31 jQuery
Vue.js自定义事件的表单输入组件方法
2018/03/08 Javascript
p5.js入门教程之小球动画示例代码
2018/03/15 Javascript
vue页面加载闪烁问题的解决方法
2018/03/28 Javascript
NestJs 静态目录配置详解
2019/03/12 Javascript
javascript实现手动点赞效果
2019/04/09 Javascript
JS实现百度搜索框关键字推荐
2020/02/17 Javascript
es6数组includes()用法实例分析
2020/04/18 Javascript
小程序瀑布流组件实现翻页与图片懒加载
2020/05/19 Javascript
python解决网站的反爬虫策略总结
2016/10/26 Python
Python中的函数式编程:不可变的数据结构
2018/10/08 Python
python内置数据类型之列表操作
2018/11/12 Python
使用Python计算玩彩票赢钱概率
2019/06/26 Python
Flask框架学习笔记之路由和反向路由详解【图文与实例】
2019/08/12 Python
Python如何使用OS模块调用cmd
2020/02/27 Python
python2.7使用scapy发送syn实例
2020/05/05 Python
python def 定义函数,调用函数方式
2020/06/02 Python
大学教师年终总结的自我评价
2013/10/29 职场文书
商铺租赁意向书
2014/04/01 职场文书
优秀党员自我评价范文
2014/09/15 职场文书
2014年仓库保管员工作总结
2014/12/03 职场文书
领导莅临指导欢迎词
2015/09/30 职场文书
PHP设计模式(观察者模式)
2021/07/07 PHP
Python基本的内置数据类型及使用方法
2022/04/13 Python
Golang 结构体数据集合
2022/04/22 Golang
Win10开机修复磁盘错误怎么跳过?Win10关闭开机磁盘检查的方法
2022/09/23 数码科技