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实现文件安全下载
Oct 09 PHP
PHP输出控制功能在简繁体转换中的应用
Oct 09 PHP
用cookies来跟踪识别用户
Oct 09 PHP
建立动态的WML站点(一)
Oct 09 PHP
php相当简单的分页类
Oct 02 PHP
PHP MSSQL 存储过程的方法
Dec 24 PHP
解析php根据ip查询所在地区(非常有用,赶集网就用到)
Jul 01 PHP
php+jQuery.uploadify实现文件上传教程
Dec 26 PHP
php通过前序遍历树实现无需递归的无限极分类
Jul 10 PHP
php模板引擎技术简单实现
Mar 15 PHP
ThinkPHP5.0框架验证码功能实现方法【基于第三方扩展包】
Mar 11 PHP
Yii Framework框架开发微信公众平台示例
Apr 26 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 变量未定义等错误的解决方法
2011/01/12 PHP
php中定时计划任务的实现原理
2013/01/08 PHP
微信公众号开发之文本消息自动回复php代码
2016/08/08 PHP
利用PHP访问带有密码的Redis方法示例
2017/02/09 PHP
php实现的pdo公共类定义与用法示例
2017/07/19 PHP
关于laravel 子查询 &amp; join的使用
2019/10/16 PHP
jquery.blockUI.js上传滚动等待效果实现思路及代码
2013/03/18 Javascript
在JavaScript并非所有的一切都是对象
2013/04/11 Javascript
如何使用jQuery Draggable和Droppable实现拖拽功能
2013/07/05 Javascript
Js表格万条数据瞬间加载实现代码
2014/02/20 Javascript
JS实现局部选择打印和局部不选择打印
2014/04/03 Javascript
jQuery实现鼠标划过修改样式的方法
2015/04/14 Javascript
javascript特效实现——当前时间和倒计时效果的简单实例
2016/07/20 Javascript
AngularJs基于角色的前端访问控制的实现
2016/11/07 Javascript
vue2笔记 — vue-router路由懒加载的实现
2017/03/03 Javascript
jQuery实现图片滑动效果
2017/03/08 Javascript
node.js-v6新版安装具体步骤(分享)
2017/09/06 Javascript
layui将table转化表单显示的方法(即table.render转为表单展示)
2019/09/24 Javascript
jquery实现上传文件进度条
2020/03/26 jQuery
解决vue项目router切换太慢问题
2020/07/19 Javascript
在vue中使用console.log无效的解决
2020/08/09 Javascript
Vue组件通信$attrs、$listeners实现原理解析
2020/09/03 Javascript
python网络编程实例简析
2014/09/26 Python
Python ftp上传文件
2016/02/13 Python
python requests库的使用
2021/01/06 Python
使用CSS3创建动态菜单效果
2015/07/10 HTML / CSS
使用CSS3来实现滚动视差效果的教程
2015/08/24 HTML / CSS
详解HTML5中CSS外观属性
2020/09/10 HTML / CSS
加拿大领先家居家具网上购物:Aosom.ca
2020/05/27 全球购物
英国领先的餐饮折扣俱乐部:Gourmet Society
2020/07/26 全球购物
食品营养与检测应届生求职信
2013/11/08 职场文书
婚前保证书
2014/04/29 职场文书
乡镇党的群众路线对照检查材料
2014/09/24 职场文书
自我推荐信格式模板
2015/03/24 职场文书
2015教师个人德育工作总结
2015/07/22 职场文书
Python实战之疫苗研发情况可视化
2021/05/18 Python