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 相关文章推荐
一个简易需要注册的留言版程序
Oct 09 PHP
php实现过滤表单提交中html标签的方法
Oct 17 PHP
PIGCMS 如何关闭聊天机器人
Feb 12 PHP
php实现简单的MVC框架实例
Sep 23 PHP
微信支付PHP SDK之微信公众号支付代码详解
Dec 09 PHP
php mysql like 实现多关键词搜索的方法
Oct 29 PHP
php基于闭包实现函数的自调用(递归)实例分析
Nov 11 PHP
实例介绍PHP中zip_open()函数用法
Feb 15 PHP
PHP针对redis常用操作实例详解
Aug 17 PHP
php中加密解密DES类的简单使用方法示例
Mar 26 PHP
PHP isset()及empty()用法区别详解
Aug 29 PHP
PHP dirname简单使用代码实例
Nov 13 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操作文件方法问答
2007/03/16 PHP
百度实时推送api接口应用示例
2014/10/21 PHP
jQuery向下滚动即时加载内容实现的瀑布流效果
2016/01/07 PHP
PHP读取大文件的几种方法介绍
2016/10/27 PHP
Prototype Selector对象学习
2009/07/23 Javascript
JavaScript 基于原型的对象(创建、调用)
2009/10/16 Javascript
JavaScript的模块化:封装(闭包),继承(原型) 介绍
2013/07/22 Javascript
JavaScript通过元素的ID和name设置样式
2014/07/08 Javascript
JS实现控制表格内指定单元格内容对齐的方法
2015/03/30 Javascript
JS实现表单验证功能(验证手机号是否存在,验证码倒计时)
2016/10/11 Javascript
JS正则截取两个字符串之间及字符串前后内容的方法
2017/01/06 Javascript
Angular.js与node.js项目里用cookie校验账户登录详解
2017/02/22 Javascript
JavaScript表单验证实现代码
2017/05/22 Javascript
jquery对table做排序操作的实例演示
2017/08/10 jQuery
vue监听scroll的坑的解决方法
2017/09/07 Javascript
详解动画插件wow.js的使用方法
2017/09/13 Javascript
实现jquery放大镜的两种方法
2018/02/22 jQuery
vue 实现购物车总价计算
2019/11/06 Javascript
[05:59]带你看看DPC的台前幕后
2021/03/11 DOTA
Python实现监控程序执行时间并将其写入日志的方法
2015/06/30 Python
解决Python pandas df 写入excel 出现的问题
2018/07/04 Python
Python实现的建造者模式示例
2018/08/06 Python
Python 输入一个数字判断成绩分数等级的方法
2018/11/15 Python
Python使用get_text()方法从大段html中提取文本的实例
2019/08/27 Python
Python Json数据文件操作原理解析
2020/05/09 Python
Python字节单位转换(将字节转换为K M G T)
2021/03/02 Python
伦敦鲜花递送:Flower Station
2021/02/03 全球购物
爱国主义演讲稿
2014/05/07 职场文书
运动会拉拉队口号
2014/06/09 职场文书
教师国庆节演讲稿范文2014
2014/09/21 职场文书
国土资源局开展党的群众路线教育实践活动整改措施
2014/09/26 职场文书
团代会邀请函
2015/02/02 职场文书
房地产财务经理岗位职责
2015/04/08 职场文书
爸爸的三轮车观后感
2015/06/16 职场文书
2016年小学党支部创先争优活动总结
2016/04/05 职场文书
Nebula Graph解决风控业务实践
2022/03/31 MySQL