php+redis实现商城秒杀功能


Posted in PHP onNovember 19, 2020

好久没来整理文章了,闲了没事写篇文章记录下php+redis实现商城秒杀功能。

1、安装redis,根据自己的php版本安装对应的redis扩展(此步骤简单的描述一下)

1.1.安装php_igbinary.dll,php_redis.dll扩展此处需要注意你的php版本如图:

php+redis实现商城秒杀功能

1.2.php.ini文件新增extension=php_igbinary.dll;extension=php_redis.dll两处扩展

ok此处已经完成第一步redis环境搭建完成看看phpinfo

php+redis实现商城秒杀功能

2、项目中实际使用redis

2.1.第一步配置redis参数如下,redis安装的默认端口为6379: 

<?php
/* 数据库配置 */
return array(
 'DATA_CACHE_PREFIX' => 'Redis_',//缓存前缀
 'DATA_CACHE_TYPE'=>'Redis',//默认动态缓存为Redis
 'DATA_CACHE_TIMEOUT' => false,
 'REDIS_RW_SEPARATE' => true, //Redis读写分离 true 开启
 'REDIS_HOST'=>'127.0.0.1', //redis服务器ip,多台用逗号隔开;读写分离开启时,第一台负责写,其它[随机]负责读;
 'REDIS_PORT'=>'6379',//端口号
 'REDIS_TIMEOUT'=>'300',//超时时间
 'REDIS_PERSISTENT'=>false,//是否长连接 false=短连接
 'REDIS_AUTH'=>'',//AUTH认证密码 
);
?>

2.2.实际函数中使用redis:

/**
  * redis连接
  * @access private
  * @return resource
  * @author bieanju
  */
 private function connectRedis(){
  $redis=new \Redis();
  $redis->connect(C("REDIS_HOST"),C("REDIS_PORT"));  
  return $redis;
 }

2.3. 秒杀的核心问题是在大并发的情况下不会超出库存的购买,这个就是处理的关键所以思路是第一步在秒杀类的先做一些基础的数据生成:

//现在初始化里面定义后边要使用的redis参数
public function _initialize(){
  parent::_initialize();
  $goods_id = I("goods_id",'0','intval');  
  if($goods_id){
   $this->goods_id = $goods_id;
   $this->user_queue_key = "goods_".$goods_id."_user";//当前商品队列的用户情况
   $this->goods_number_key = "goods".$goods_id;//当前商品的库存队列
  }
  $this->user_id = $this->user_id ? $this->user_id : $_SESSION['uid'];  
 }

2.4. 第二步就是关键所在,用户在进入商品详情页前先将当前商品的库存进行队列存入redis如下:

/**
 * 访问产品前先将当前产品库存队列
 * @access public
 * @author bieanju
 */
 public function _before_detail(){
  $where['goods_id'] = $this->goods_id;
  $where['start_time'] = array("lt",time());
  $where['end_time'] = array("gt",time());
  $goods = M("goods")->where($where)->field('goods_num,start_time,end_time')->find();
  !$goods && $this->error("当前秒杀已结束!");
  if($goods['goods_num'] > $goods['order_num']){
   $redis = $this->connectRedis();
   $getUserRedis = $redis->hGetAll("{$this->user_queue_key}");
   $gnRedis = $redis->llen("{$this->goods_number_key}");
   /* 如果没有会员进来队列库存 */
   if(!count($getUserRedis) && !$gnRedis){   
    for ($i = 0; $i < $goods['goods_num']; $i ++) {
     $redis->lpush("{$this->goods_number_key}", 1);
    }
   }
   $resetRedis = $redis->llen("{$this->goods_number_key}");
   if(!$resetRedis){
    $this->error("系统繁忙,请稍后抢购!");
   }
  }else{
   $this->error("当前产品已经秒杀完!");
  }
   
 }

接下来要做的就是用ajax来异步的处理用户点击购买按钮进行符合条件的数据进入购买的排队队列(如果当前用户没在当前产品用户的队列就进入排队并且pop一个库存队列,如果在就抛出,):

/**
  * 抢购商品前处理当前会员是否进入队列
  * @access public
  * @author bieanju
  */
 public function goods_number_queue(){
  !$this->user_id && $this->ajaxReturn(array("status" => "-1","msg" => "请先登录"));
  $model = M("flash_sale");
  $where['goods_id'] = $this->goods_id;
  $goods_info = $model->where($where)->find();
  !$goods_info && $this->error("对不起当前商品不存在或已下架!"); 
  /* redis 队列 */ 
  $redis = $this->connectRedis();
  /* 进入队列 */
  $goods_number_key = $redis->llen("{$this->goods_number_key}");
  if (!$redis->hGet("{$this->user_queue_key}", $this->user_id)) {
   $goods_number_key = $redis->lpop("{$this->goods_number_key}");
  }
   
  if($goods_number_key){
   // 判断用户是否已在队列
   if (!$redis->hGet("{$this->user_queue_key}", $this->user_id)) {
    // 插入抢购用户信息
    $userinfo = array(
     "user_id" => $this->user_id,
     "create_time" => time()
    );    
    $redis->hSet("{$this->user_queue_key}", $this->user_id, serialize($userinfo));
    $this->ajaxReturn(array("status" => "1"));
   }else{
    $modelCart = M("cart");
    $condition['user_id'] = $this->user_id;
    $condition['goods_id'] = $this->goods_id;
    $condition['prom_type'] = 1;
  $cartlist = $modelCart->where($condition)->count();
    if($cartlist > 0){
     $this->ajaxReturn(array("status" => "2"));
    }else{
     
     $this->ajaxReturn(array("status" => "1"));
     
    }
     
   }
    
  }else{
   $this->ajaxReturn(array("status" => "-1","msg" => "系统繁忙,请重试!"));
  }
 }

附加一个调试的函数,删除指定队列值:

public function clearRedis(){
   set_time_limit(0);
   $redis = $this->connectRedis();
   //$Rd = $redis->del("{$this->user_queue_key}");
   $Rd = $redis->hDel("goods49",'用户id'');
   $a = $redis->hGet("goods_49_user", '用户id');
   if(!$a){
    dump($a);
   }
   
   if($Rd == 0){
    exit("Redis队列已释放!");   
   }
}

走到此处的时候秒杀的核心基本就完了,细节还需要自己在去完善,像购物车这边的处理还有订单的处理,好吧开始跑程序利用apache自身的ab可以进行简单的模拟并发测试如下:

php+redis实现商城秒杀功能

跑起来,我擦跑步起来redis没有任何反应,此时还少一步重要的步骤就是开启redis服务,请根据自己的系统下一个redisbin_x32或者redisbin_x64的redis服务管理工具,点击redis-server.exe,ok至此全部完成如下图:

php+redis实现商城秒杀功能

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
php 判断访客是否为搜索引擎蜘蛛的函数代码
Jul 29 PHP
详解php的魔术方法__get()和__set()使用介绍
Sep 19 PHP
PHP生成迅雷、快车、旋风等软件的下载链接代码实例
May 12 PHP
一组PHP加密解密函数分享
Jun 05 PHP
php中文繁体和简体相互转换的方法
Mar 21 PHP
php抛出异常与捕捉特定类型的异常详解
Oct 26 PHP
浅谈php(codeigniter)安全性注意事项
Apr 06 PHP
phpmyadmin下载、安装、配置教程
May 16 PHP
thinkPHP5分页功能实现方法分析
Oct 25 PHP
浅谈PHP各环境下的伪静态配置
Mar 13 PHP
Yii框架中用response保存cookie,用request读取cookie的原理解析
Sep 04 PHP
基于PHP的登录和注册的功能的实现
Aug 06 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
浅析PHP类的反射来实现依赖注入过程
Feb 06 #PHP
You might like
用PHP连接MySQL代码的参数说明
2008/06/07 PHP
PHP批量上传图片的具体实现方法介绍.
2014/02/26 PHP
PHP读取zip文件的方法示例
2016/11/17 PHP
PHP更安全的密码加密机制Bcrypt详解
2017/06/18 PHP
Laravel学习笔记之Artisan命令生成自定义模板的方法
2018/11/22 PHP
jQuery EasyUI API 中文文档 - Menu菜单
2011/10/03 Javascript
按下回车键指向下一个位置的一个函数代码
2014/03/10 Javascript
jquery 按键盘上的enter事件
2014/05/11 Javascript
浅谈jQuery中对象遍历.eq().first().last().slice()方法
2014/11/26 Javascript
使用jQuery处理AJAX请求的基础学习教程
2016/05/10 Javascript
JS打印组合功能
2016/08/04 Javascript
webpack处理 css\less\sass 样式的方法
2017/08/21 Javascript
使用mock.js随机数据和使用express输出json接口的实现方法
2018/01/07 Javascript
详解react组件通讯方式(多种)
2020/05/06 Javascript
解决vue-photo-preview 异步图片放大失效的问题
2020/07/29 Javascript
[02:57]DOTA2亚洲邀请赛 SECRET战队出场宣传片
2015/02/07 DOTA
[04:11]DOTA2上海特级锦标赛主赛事首日TOP10
2016/03/03 DOTA
python实现超简单端口转发的方法
2015/03/13 Python
简单介绍Python的轻便web框架Bottle
2015/04/08 Python
OpenCV实现人脸识别
2017/04/07 Python
Python网络爬虫神器PyQuery的基本使用教程
2018/02/03 Python
对Python3 * 和 ** 运算符详解
2019/02/16 Python
Pyecharts地图显示不完成问题解决方案
2020/05/11 Python
美国蔬菜和植物种子公司:Burpee
2017/02/01 全球购物
中国电子产品批发商/跨境电商/外贸网:Sunsky-online
2020/04/20 全球购物
2014年上半年工作自我评价
2014/01/18 职场文书
优秀通讯员事迹材料
2014/01/28 职场文书
餐饮商业计划书范文
2014/04/29 职场文书
安全技术说明书
2014/05/09 职场文书
开展读书活动总结
2014/06/30 职场文书
2014年客户经理工作总结
2014/11/20 职场文书
法院执行局工作总结
2015/08/11 职场文书
MySQL sql_mode的使用详解
2021/05/08 MySQL
pytorch加载预训练模型与自己模型不匹配的解决方案
2021/05/13 Python
pytorch交叉熵损失函数的weight参数的使用
2021/05/24 Python
JS setTimeout与setInterval的区别
2022/04/20 Javascript