php+redis实现商城秒杀功能

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

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

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

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

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

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可以进行简单的模拟并发测试如下:

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

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

(0)

相关推荐

  • PHP多线程模拟实现秒杀抢单

    应集团要求给服务号做了个抢单秒杀的功能,需要对秒杀做个测试,想试试PHP多线程,就模拟了下抢单功能. 先说秒杀模块的思路: 正常情况下的用户秒杀操作 1.发起秒杀请求 2.进入秒杀队列 3.随机滞后 1 - 2 秒进行秒杀结果查询请求(算是变相分流吧) 4.成功则生成订单 5.返回结果 以下是模拟秒杀的代码: <?php set_time_limit(0); /** * 线程的执行任务 */ class Threadrun extends Thread { public $url; public

  • php微信公众号开发之秒杀

    本文实例为大家分享了php微信公众号秒杀功能的具体代码,供大家参考,具体内容如下 数据库小知识点: strtotime:将字符串转换成时间 time():时间函数,调用系统当前时间 核心代码: $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); $fromUsername = $postObj->FromUserName; $toUsername = $postObj->ToUserNa

  • PHP+JS实现的商品秒杀倒计时用法示例

    本文实例讲述了PHP+JS实现的商品秒杀倒计时用法.分享给大家供大家参考,具体如下: <?php //php的时间是以秒算.js的时间以毫秒算 date_default_timezone_set('PRC'); //date_default_timezone_set("Asia/Hong_Kong");//地区 //配置每天的活动时间段 $starttimestr = "2016-3-29 8:10:00"; $endtimestr = "2016-

  • yii框架redis结合php实现秒杀效果(实例代码)

    废话不多说了,直接给大家贴代码了,具体代码如下所示: <?php namespace backend\controllers; use Yii; use yii\web\Controller; /** * */ class GoodsController extends Controller { public $enableCsrfValidation=false; public function actionInfo() { $data=yii::$app->db->createCom

  • PHP 类商品秒杀计时实现代码

    要求要有小时分钟秒的实时倒计时的显示,用户端修改日期时间不会影响到倒计时的正常显示(也就是以服务器时间为准). 其实这和很多的考试等系统的时间限制功能同样的要求. 总不能用ajax每秒都获取服务器时间吧,所以实时倒计时一定要用javascript实现.这很简单,网上一大把的例子. 现在问题是解决用户端修改日期时间对我们的显示的影响. 解决的办法是计算出用户端的时间和服务器的时间差,这样问题的完成解决了. 这样只需要运行一次php,实时倒计时的时间就和服务器的时间同步了. 理论是同步的,但实际测试

  • php结合redis实现高并发下的抢购、秒杀功能的实例

    抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个: 1 高并发对数据库产生的压力 2 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易想到用缓存来处理抢购,避免直接操作数据库,例如使用Redis. 重点在于第二个问题 常规写法: 查询出对应商品的库存,看是否大于0,然后执行生成订单等操作,但是在判断库存是否大于0处,如果在高并发下就会有问题,导致库存量出现负数 <?php $conn=mysql_connect("localho

  • php解决抢购秒杀抽奖等大流量并发入库导致的库存负数的问题

    我们知道数据库处理sql是一条条处理的,假设购买商品的流程是这样的: sql1:查询商品库存 if(库存数量 > 0) { //生成订单... sql2:库存-1 } 当没有并发时,上面的流程看起来是如此完美,假设同时两个人下单,而库存只有1个了,在sql1阶段两个人查询到的库存都是>0的,于是最终都执行了sql2,库存最后变为-1,超售了,要么补库存,要么等用户投诉吧. 解决这个问题比较流行的思路: 1.用额外的单进程处理一个队列,下单请求放到队列里,一个个处理,就不会有并发的问题了,但是要

  • php+redis实现商城秒杀功能

    好久没来整理文章了,闲了没事写篇文章记录下php+redis实现商城秒杀功能. 1.安装redis,根据自己的php版本安装对应的redis扩展(此步骤简单的描述一下) 1.1.安装php_igbinary.dll,php_redis.dll扩展此处需要注意你的php版本如图: 1.2.php.ini文件新增extension=php_igbinary.dll;extension=php_redis.dll两处扩展 ok此处已经完成第一步redis环境搭建完成看看phpinfo 2.项目中实际使

  • 基于redis分布式锁实现秒杀功能

    最近在项目中遇到了类似"秒杀"的业务场景,在本篇博客中,我将用一个非常简单的demo,阐述实现所谓"秒杀"的基本思路. 业务场景 所谓秒杀,从业务角度看,是短时间内多个用户"争抢"资源,这里的资源在大部分秒杀场景里是商品:将业务抽象,技术角度看,秒杀就是多个线程对资源进行操作,所以实现秒杀,就必须控制线程对资源的争抢,既要保证高效并发,也要保证操作的正确. 一些可能的实现 刚才提到过,实现秒杀的关键点是控制线程对资源的争抢,根据基本的线程知识,可

  • redis使用watch秒杀抢购实现思路

    本文实例为大家分享了redis使用watch秒杀抢购的具体代码,供大家参考,具体内容如下 1.使用watch,采用乐观锁 2.不使用悲观锁,因为等待时间非常长,响应慢 3.不使用队列,因为并发量会让队列内存瞬间升高 代码: import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import redis.clients.jedis.Jedis; /** * redis测试抢购 * *

  • Redis实战之商城购物车功能的实现代码

    目标 利用Redis实现商城购物车功能. 功能 根据用户编号查询购物车列表,且各个商品需要跟在对应的店铺下:统计购物车中的商品总数:新增或删减购物车商品:增加或减少购物车中的商品数量. 分析 Hash数据类型:值为多组映射,相当于JAVA中的Map.适合存储对象数据类型.因为用户ID作为唯一的身份标识,所以可以把模块名称+用户ID作为Redis的键:商品ID作为商品的唯一标识,可以把店铺编号+商品ID作为Hash元素的键,商品数量为元素的值. 代码实现 控制层 package com.shopp

  • vue实现商城秒杀倒计时功能

    vue实现商城秒杀倒计时功能,效果图如下所示: template代码 <div> <div class="component-wrapper" id="flash-sale"> <div class="sale-header"> <div class="countdown-zone"> <div class="countdown-prefix">限

  • JS实现商城秒杀倒计时功能(动态设置秒杀时间)

    一年一度的双十二如期而至,今天的你买买买了吗,下面小编给大家分享一个动态秒杀倒计时功能. 效果图 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="noStart">活动未开始</div> <div clas

  • Java使用Redis实现秒杀功能

    秒杀功能 秒杀场景现在已经非常常见了,各种电商平台都有秒杀的产品,接下来我们模拟一个秒杀的项目,最终能够确保高并发下的线程安全.界面比较简单,但是功能基本实现. 界面 点击"秒杀点我"按钮后台就会输出秒杀结果. 第一版 使用Redis缓存数据库,使用一个key-value存储秒杀商品数量,使用set集合存储秒杀成功的用户.我们以商品0101为示例,设置商品的初始数量为200件.不考虑并发问题,实现功能. html.jsp.servlet文件不重要省略. package com.redi

  • 使用Redis实现秒杀功能的简单方法

    1. 怎样预防数据库超售现象 设置数据库事务的隔离级别为Serializable(不可用) Serializable就是让数据库去串行化的去执行事务,一个事务执行完才能去执行下一个事务,效率太慢 在数据表上设置乐观锁字段,例如设置版本号(version) 不同事务在执行更新操作时,需要先判断一下版本号是否已被修改 代码实现乐观锁流程 1.1. 什么表需要设置乐观锁 出现同时修改同一条记录的业务,相应的数据表要设置乐观锁 不会出现同时修改同一记录的数据库,就不需要设置乐观锁 2. 利用Redis防

  • 如何通过SpringBoot实现商城秒杀系统

    这篇文章主要介绍了如何通过SpringBoot实现商城秒杀系统,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 学习自:地址 1.主要流程 1.1数据库: 1.2 环境 window下:Zookeeper,Redis,rabbitmq-server.jdk1.8以上. 1.3 介绍 这里只做秒杀部分功能,其他功能不会涉及.项目运行后可访问秒杀商品页面 当用户没登陆,点击详情会跳转到登陆页面. 用户登陆后可以查看商品的详情并进行抢购. 注意,用户对

随机推荐