thinkphp+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: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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:

1
2
3
4
5
6
7
8
9
10
11
/**
         * 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,秒杀的核心问题是在大并发的情况下不会超出库存的购买,这个就是处理的关键所以思路是第一步在秒杀类的先做一些基础的数据生成:

1
2
3
4
5
6
7
8
9
10
11
//现在初始化里面定义后边要使用的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如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
     * 访问产品前先将当前产品库存队列
     * @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一个库存队列,如果在就抛出,):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
      * 抢购商品前处理当前会员是否进入队列
      * @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"  =>  "系统繁忙,请重试!" ));
         }
     }

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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至此全部完成如下图:

人已赞赏
PHPthinkPHP

ThinkPHP5.0 实现 app微信支付功能 技术标签: 微信支付

2020-9-28 10:32:41

RedisthinkPHP

高并发简单解决方案————redis队列缓存+mysql 批量入库(ThinkPhP)

2020-10-6 15:44:47

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索