Thinkphp redis秒杀功能

本文介绍如何使用Redis实现秒杀系统的库存控制与排队机制。包括安装配置Redis、配置项目使用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两处扩展

http://windows.php.net/downloads/pecl/releases/igbinary/

http://windows.php.net/downloads/pecl/releases/redis/2.2.7/

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至此全部完成如下图:

一个人静静坐在电脑面前写代码的感觉,那是什么感觉?那是武林高手闭关修炼的感觉。

转载于:https://www.cnblogs.com/zhang-bin/p/9668012.html

### 回答1: ThinkPHP6 Redis 秒杀是指使用 Redis 作为缓存和计数器,实现高并发下的秒杀功能。在秒杀活动中,由于用户量巨大,瞬间的请求量会超过服务器的承受能力,导致系统崩溃或者响应时间过长。而使用 Redis 作为缓存和计数器,可以将请求分散到多个服务器上,避免单点故障,提高系统的稳定性和响应速度。同时,Redis 的原子性操作可以保证秒杀的正确性和公平性,避免出现超卖或者重复购买等问题。 ### 回答2: ThinkPHP6是一款轻量级、高效的PHP框架,提供了非常多的开发组件和工具,支持多种后端数据库和缓存系统。而Redis又是一种高性能的key-value数据存储系统,非常适合用于数据缓存、消息队列、计数器等场景。结合ThinkPHP6框架和Redis缓存系统,可以实现高并发并且高效的秒杀系统。 在ThinkPHP6中使用Redis缓存,需要先配置好redis的连接信息。在config目录下的cache.php文件中,可以进行相关配置,例如: ```php return [ // 缓存配置 'default' => env('cache.driver', 'file'), 'stores' => [ 'file' => [ 'driver' => 'file', 'path' => runtime_path('cache'), ], 'redis' => [ 'driver' => 'redis', 'host' => '127.0.0.1', 'port' => 6379, 'password' => '', 'select' => 0, 'timeout' => 0, 'expire' => 3600, 'persistent' => false, 'prefix' => '', ], ], ]; ``` 然后,在控制器中处理秒杀请求时,可以使用Redis中的事务和锁机制,保证秒杀过程的原子性和线程安全。例如: ```php public function seckill() { $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $product_id = input('product_id'); $stock_key = 'product:' . $product_id . ':stock'; $user_id = session('user_id'); $buy_key = 'product:' . $product_id . ':user:' . $user_id . ':buy'; // 开启事务 $redis->multi(); // 判断库存是否充足 if ($redis->get($stock_key) <= 0) { $redis->discard(); return json(['code' => -1, 'msg' => '秒杀失败,商品已售罄']); } // 判断用户是否已经购买 if ($redis->get($buy_key) > 0) { $redis->discard(); return json(['code' => -1, 'msg' => '秒杀失败,您已经购买过该商品']); } // 减少库存 $redis->decr($stock_key); // 记录用户已经购买 $redis->incr($buy_key); // 执行事务 $result = $redis->exec(); if ($result) { return json(['code' => 0, 'msg' => '秒杀成功,请尽快支付']); } else { return json(['code' => -1, 'msg' => '秒杀失败,请重新尝试']); } } ``` 以上代码实现了利用Redis的事务和锁机制,在秒杀请求到来时进行秒杀过程的原子性操作。其中,使用$product_id和$user_id作为key,分别表示商品库存和用户已购买数量,用以保证并发请求的正确性和安全性。 综上所述,ThinkPHP6和Redis的结合使用非常灵活和高效,可以实现各种高并发的应用场景,包括秒杀系统。但同时,由于秒杀系统的特点,需要在代码实现中考虑更多的性能优化和安全性措施,避免出现重大错误和损失。 ### 回答3: ThinkPHP6是一个轻量级PHP框架,可以帮助开发者快速建立Web应用程序。Redis是一个高性能的非关系型内存键值数据库,可以用于存储数据、缓存和消息代理等业务场景。 秒杀是电商业务中常见的一种活动,即在特定时间内限量销售某种商品,通常会引起用户的强烈购买欲望,需要支持高并发、高可靠和高性能的处理方式。 使用ThinkPHP6结合Redis来实现秒杀系统可以采用如下架构: 1. 前端展示层:实现商品展示、倒计时展示、抢购按钮和下单逻辑。 2. 应用层:利用TP6的控制器处理用户请求,调用服务层处理秒杀请求,还可以使用TP6的事件管理器做进一步优化,比如从Redis缓存读取秒杀商品信息,进行幂等性判断等。 3. 服务层:处理商品的库存、订单量、用户秒杀记录等业务逻辑,使用Redis记录秒杀商品的状态(秒杀开始/结束),并提供限流措施,如乐观锁、悲观锁、分布式锁等。 4. 数据层:使用Redis存储秒杀商品信息、秒杀商品数量、用户秒杀信息和订单信息等数据。此处需注意数据存储的结构设计和Redis的线程模型,需要合理利用Redis的命令和批量写入性能。 总的来说,通过结合ThinkPHP6和Redis的强大特性,可以快速搭建一个高性能、可扩展的秒杀系统,提高用户体验,增强品牌价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值