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 相关文章推荐
真正根据utf8编码的规律来进行截取字符串的函数(utf8版sub_str )
Oct 24 PHP
memcache命令启动参数中文解释
Jan 13 PHP
ThinkPHP使用心得分享-ThinkPHP + Ajax 实现2级联动下拉菜单
May 15 PHP
php不使用copy()函数复制文件的方法
Mar 13 PHP
php快速查找数据库中恶意代码的方法
Apr 01 PHP
php中preg_match的isU代表什么意思
Oct 01 PHP
php多线程并发实现方法
Sep 30 PHP
原生JS实现Ajax通过POST方式与PHP进行交互的方法示例
May 12 PHP
PHP基于DateTime类解决Unix时间戳与日期互转问题【针对1970年前及2038年后时间戳】
Jun 13 PHP
php进程daemon化的正确实现方法
Sep 06 PHP
laravel 解决paginate查询多个字段报错的问题
Oct 22 PHP
php 下 html5 XHR2 + FormData + File API 上传文件操作实例分析
Feb 28 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
mysqli_set_charset和SET NAMES使用抉择及优劣分析
2013/01/13 PHP
解析PHP多种序列化与反序列化的方法
2013/06/06 PHP
preg_match_all使用心得分享
2014/01/31 PHP
一个经典实用的PHP图像处理类分享
2014/11/18 PHP
详解yii2使用多个数据库的案例
2017/06/16 PHP
json数据处理技巧(字段带空格、增加字段、排序等等)
2013/06/14 Javascript
JQuery-tableDnD 拖拽的基本使用介绍
2013/07/04 Javascript
Angular+Node生成随机数的方法
2017/06/16 Javascript
动态统计当前输入内容的字节、字符数的实例详解
2017/10/27 Javascript
Angular 4根据组件名称动态创建出组件的方法教程
2017/11/01 Javascript
js实现前面自动补全位数的方法
2018/10/10 Javascript
使用Vue调取接口,并渲染数据的示例代码
2019/10/28 Javascript
vue 表单输入框不支持focus及blur事件的解决方案
2020/11/17 Vue.js
解决vue下载后台传过来的乱码流的问题
2020/12/05 Vue.js
Vue中避免滥用this去读取data中数据
2021/03/02 Vue.js
[35:43]2018DOTA2亚洲邀请赛 4.1 小组赛B组 paiN vs Effect
2018/04/03 DOTA
Swift中的协议(protocol)学习教程
2016/07/08 Python
详解利用django中间件django.middleware.csrf.CsrfViewMiddleware防止csrf攻击
2018/10/09 Python
Python flask框架post接口调用示例
2019/07/03 Python
vscode 配置 python3开发环境的方法
2019/09/19 Python
python可视化实现KNN算法
2019/10/16 Python
Python-jenkins 获取job构建信息方式
2020/05/12 Python
将HTML5 Canvas的内容保存为图片借助toDataURL实现
2013/05/20 HTML / CSS
Html5移动端div固定到底部实现底部导航条的几种方式
2021/03/09 HTML / CSS
优秀共产党员先进事迹
2014/01/27 职场文书
节约电力资源的建议书
2014/03/12 职场文书
六一儿童节主持词
2014/03/21 职场文书
百货商场楼层班组长竞聘书
2014/03/31 职场文书
Nginx + consul + upsync 完成动态负载均衡的方法详解
2021/03/31 Servers
html+css 实现简易导航栏功能
2021/04/07 HTML / CSS
Axios取消重复请求的方法实例详解
2021/06/15 Javascript
Go语言基础map用法及示例详解
2021/11/17 Golang
PostgreSQL自动更新时间戳实例代码
2021/11/27 PostgreSQL
分布式架构Redis中有哪些数据结构及底层实现原理
2022/03/13 Redis
maven 解包依赖项中的文件的解决方法
2022/07/15 Java/Android
windows10 家庭版下FTP服务器搭建教程
2022/08/05 Servers