Redis 秒杀系统实战代码

本来这个是想弄到博客的实验室里面的,但是测试后服务器顶不住测试,容易崩就放弃了

下面是具体的代码,大概主要的注释都有了,有疑问留言哈哈

    /** 
    *   使用 Predis 这个库操作redis
    *   $request  和 $this->success()这是tp5框架和自己封装的json数据返回  自己定义吧
    */

    /**
     * 系统前缀
     */
    protected $system_prefix='cc_test_';

    /**
     * 测试时长 5分钟
     * @var int
     */
    protected $second = 300;

    /**
     * Redis 秒杀测试初始化
     * @param Request $request
     */
    public function redisTestShopInit(Request $request)
    {
        /**
         * 当前请求ip
         */
        $current_ip = $_SERVER['REMOTE_ADDR'];

        /**
         * 先定义好每个测试员的key的前缀
         */
        $prefix = $request->post('prefix');
        if(strpos($prefix,'_')===false){
            $prefix .= '_';
        }
        /**
         * 定义商品数量
         */
        $shop_num=$request->post('shop_num');
        /**
         * 测试商品名称
         */
        $shop_name = $request->post('shop_name');
        /**
         * 秒杀时间长度:秒
         */
        $second_len=$request->post('second_len');
        if($second_len>300){//最大5分钟
            $second_len=300;
        }
        $second_len=300;//这里我默认5分钟好了
        $system_prefix =$this->system_prefix;
        /**
         * 测试者的前缀
         * cc_test_192.168.1.106_
         */
        $tester_prefix = $system_prefix . $current_ip . '_';//cc_test_192.168.1.106_

        /**
         *  这里好像跟我后面的有点出入, 还是留着吧 看看就行
         *
         * 1.把商品放入hash中 假设key为 xxxxxx_googd:1
         * 2.先缓存待秒杀的商品id在 list中
         * 3.设置商品库存在sorted set
         */

        /**
         * redis list  商品列表key
         */
        $redis_shop_list_key=$tester_prefix.'shop_list';
        /**
         * 商品库存key  score_set  暂时没用  后面需要库存再考虑
         */
        $redis_shop_zset_key=$tester_prefix.'shop_inventory_score_set';

        $client = new \Predis\Client();

        if($client->exists($redis_shop_list_key)){
            $this->success('商品已经初始化了,请勿重复','',4010);
        }
        //初始化商品
        for ($i = 0; $i < $shop_num; $i++) {
            $tmp_shop_key=$tester_prefix.'test_shop:'.$i;

            $tmp_shop_hash_detail_key=$tmp_shop_key;
            $tmp_shop_hash_detail_title=$shop_name.'-'.$i;
            $tmp_shop_hash_detail_num=10;
            $tmp_shop_hash_detail_content='我是  '.$shop_name.'  商品的详情'.$i.'-'.$i.'-'.$i.'-'.$i;

            //把商品详情记录在 hash中 定义key,并把key放入 list中
            $client->hset($tmp_shop_hash_detail_key, 'id', $i);
            $client->hset($tmp_shop_hash_detail_key, 'title', $tmp_shop_hash_detail_title);
            $client->hset($tmp_shop_hash_detail_key, 'content', $tmp_shop_hash_detail_content);
            $client->hset($tmp_shop_hash_detail_key, 'num', $tmp_shop_hash_detail_num);//库存

            $client->lpush($redis_shop_list_key, $tmp_shop_key);//把秒杀商品的key放进list中

            //设置商品库存  (把库存存在scoreset中这中方法有待考察,觉得必要性不强)
//            $client->zadd($redis_shop_zset_key, [$tmp_shop_hash_detail_key => 10]);

            //把详情的也设置超时时长  提供给别人的测试需要设置超时时长,正式就不需要了
            $client->expire($tmp_shop_hash_detail_key, $second_len);
        }
        //设置秒杀列表存在时长 
        $client->expire($redis_shop_list_key, $second_len);

        $shop_lists= $client->lrange($redis_shop_list_key, 0, -1);
        $data = array();
        foreach ($shop_lists as $ind=>$shop_key) {
            $data[$ind]['key']=$shop_key;
            $data[$ind]['title']=$client->hget($shop_key,'title');
            $data[$ind]['num']=$client->hget($shop_key,'num');
        }
        $this->success('success',$data,200);
    }

    /**
     * 获取秒杀商品列表
     */
    public function redisTestGetSeckillShopList(Request $request)
    {
        $current_ip =$_SERVER['REMOTE_ADDR'];
        $system_prefix =$this->system_prefix;

        $tester_prefix = $system_prefix . $current_ip . '_';//cc_test_192.168.1.106_
        $redis_shop_list_key=$tester_prefix.'shop_list';

        $client = new \Predis\Client();
        $page = $request->post('page');
        $page=$page<0?0:$page;
        $start=($page-1)*10;
        $end=$start+10;

        $shop_lists= $client->lrange($redis_shop_list_key, $start, $end);
        $data = array();
        foreach ($shop_lists as $ind=>$shop_key) {
            $data[$ind]['key']=$shop_key;
            $data[$ind]['title']=$client->hget($shop_key,'title');
            $data[$ind]['num']=$client->hget($shop_key,'num');
        }

        $this->success('success', $data,200);
    }

    /**
     * 商品购买
     * @param Request $request
     */
    public function redisTestBuyShop(Request $request)
    {
        $shop_hash_key = $request->post('shop_key');
        $buyer_ip = $_SERVER['REMOTE_ADDR'];
        $second=$this->second;
        /**
         * 1.先判断商品是否过期,库存是否足够
         * 2.用户购买了商品,商品库存-1
         * 3.把商品key和用户放在一个list中,这个list就是购买成功人员的记录,后续需要存入数据库做持久化保存
         *
         */

        //score set 这个可以用作库存管理,后续再弄吧

        $client = new \Predis\Client();
        $tester_prefix = $this->system_prefix . $buyer_ip . '_';
        $redis_shop_list_key=$tester_prefix.'shop_list';
//        $client->multi();
        if(!$client->exists($redis_shop_list_key)){
            $this->success('秒杀活动过期','',4001);
        }
        if(!$client->exists($shop_hash_key)){
            $this->success('商品过期','',4002);
        }
        $num=$client->hget($shop_hash_key,'num');
        if($num<=0){
            $client->hset($shop_hash_key, 'num', 0);
            $this->success('库存不足','',4003);//库存没了
        }
        $client->hincrby($shop_hash_key, 'num', -1);

        $buyer_list=$this->system_prefix.'ip:'.$buyer_ip.'_buyer_list';

        $order_num = mt_rand(10000000, 99999999);
        $buyer_hash_key=$this->system_prefix.'buyer_hash_buyer.ip:'.$buyer_ip.'_ordernum:'.$order_num;//为每个购买者生成一个订单号

        $client->hset($buyer_hash_key, 'created_at', time());
        $client->hset($buyer_hash_key, 'shop_key',$shop_hash_key);
        $client->hset($buyer_hash_key, 'buy_num',1);
        $client->hset($buyer_hash_key, 'buyer_ip',$buyer_ip);
        $client->expire($buyer_hash_key, $second);

        //记录购买者购买信息key在列表中
        $client->lpush($buyer_list, $buyer_hash_key);
        $client->expire($buyer_list, $second);
//        $client->exec();
        //秒杀结束后需要把这些购买成功的存入数据库中
        $this->success('购买成功', $client->hgetall($buyer_hash_key));
    }
    /**
     * 秒杀系统测试:
     * -------------------------------------------------------------------------
     * 第一次JMeter测试 :   200并发 测试10次,没问题,不会超出库存
     * 第二次JMeter测试 :   500并发 测试10次,没问题,不会超出库存
     * 第三次JMeter测试 :   1000并发 测试10次,失败 会超出库存
     * 修改代码第四次JMeter测试 :   1000并发 测试10次,成功,不会超出库存
     * 第四次JMeter测试 :   1000并发 测试50次,成功,不会超出库存
     * 第五次JMeter测试 :   5000并发 测试50次, 成功 不会超出库存 但是有些请求直接崩了(这个先暂时不管了哈哈)
     * 第六次JMeter测试 :   10000并发 测试10次, 成功 不会超出库存 服务器崩了
     * --------------------------------------------------------------------------
     */

这只是我自己的一个思路而已 , 也不知道是不是这样用哈哈,有错误或者不对欢迎留言指出

个人博客