thinkphp+redis实现秒杀功能

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: thinkphp+redis实现秒杀功能

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

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

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

  01ebd755782e4c909dad0843d3544acf.jpeg

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

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

01ebd755782e4c909dad0843d3544acf.jpeg

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

01ebd755782e4c909dad0843d3544acf.jpeg

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

01ebd755782e4c909dad0843d3544acf.jpeg

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



相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
存储 NoSQL PHP
如何用Redis高效实现点赞功能?用Set?还是Bitmap?
在众多软件应用中,点赞功能几乎成为标配。本文从实际需求出发,探讨如何利用 Redis 的 `Set` 和 `Bitmap` 数据结构设计高效点赞系统,分析其优缺点,并提供 PHP 实现示例。通过对比两种方案,帮助开发者选择最适合的存储方式。
28 3
|
6月前
|
消息中间件 NoSQL Java
Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)
Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)
242 0
|
2月前
|
缓存 分布式计算 NoSQL
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
34 2
|
2月前
|
SQL 分布式计算 NoSQL
大数据-42 Redis 功能扩展 发布/订阅模式 事务相关的内容 Redis弱事务
大数据-42 Redis 功能扩展 发布/订阅模式 事务相关的内容 Redis弱事务
25 2
|
2月前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
75 2
|
4月前
|
缓存 NoSQL 测试技术
【Azure Redis 缓存】Azure Redis 功能性讨论三: 调优参数配置
【Azure Redis 缓存】Azure Redis 功能性讨论三: 调优参数配置
|
4月前
|
存储 缓存 监控
【Azure Redis 缓存】Azure Redis 功能性讨论二
【Azure Redis 缓存】Azure Redis 功能性讨论二
【Azure Redis 缓存】Azure Redis 功能性讨论二
|
3月前
|
消息中间件 NoSQL Go
PHP转Go系列 | ThinkPHP与Gin框架之Redis延时消息队列技术实践
【9月更文挑战第7天】在从 PHP 的 ThinkPHP 框架迁移到 Go 的 Gin 框架时,涉及 Redis 延时消息队列的技术实践主要包括:理解延时消息队列概念,其能在特定时间处理消息,适用于定时任务等场景;在 ThinkPHP 中使用 Redis 实现延时队列;在 Gin 中结合 Go 的 Redis 客户端库实现类似功能;Go 具有更高性能和简洁性,适合处理大量消息。迁移过程中需考虑业务需求及系统稳定性。
|
4月前
|
存储 缓存 NoSQL
【Azure Redis 缓存】Azure Redis功能性讨论
【Azure Redis 缓存】Azure Redis功能性讨论