高并发场景下的缓存使用误区

看到此文,是否觉得体内洪荒之力爆发,饥渴难耐想吐槽、情不自禁想捐赠
本文为原创文章,尊重辛勤劳动,可以免费摘要、推荐或聚合,亦可完整转载,但完整转载需要标明原出处,违者必究。

支付宝微  信

十一聚会,某谈及人生理想。我要的是“地位,身份和爱情!”,其实就是金钱,面子和美女。O.O

正文:

9月份,连续两天(AB两天)线上出现业务故障,redis监控曲线瞬间上涨。

业务场景:一千万UV / 日

redis监控曲线(修改图片好麻烦,曲线意会下吧~):redis-clients-num redis-command redis-key redis-read redis-write

 

redis日志:

Screen Shot 2015-10-08 at 12.49.37

业务代码:

        (self::$_debug) ? Helper_Log::writeApplog('newFeatureOfMixedFeed', "初始化配比数据.") : null;
        if(self::$_redisDb == null) {
            self::$_redisDb       = new Comm_Redisdb(self::REDIS_SERVER);
        }
        /** 判断是否有缓存数据 **/
        $total              = self::$_redisDb->lSize(Conf_Sailmixfeed::REDIS_CONF_RATIO_KEY, array());
        if($total) {
            /** 有缓存数据**/
            $tmp            = self::$_redisDb->lRange(self::REDIS_CONF_RATIO_KEY, array(), 0, 0);
            self::$_ratio   = $tmp[0];
            (self::$_debug) ? Helper_Log::writeApplog('newFeatureOfMixedFeed', "有配比的Redis缓存数据:" . var_export(self::$_ratio, true)) : null;
        } else {
            (self::$_debug) ?  Helper_Log::writeApplog('newFeatureOfMixedFeed', "Redis中无配比数据") : null;
            /** 无缓存数据 => 设置缓存 **/
            /** 读库 **/ 
            $results = ModelMixed_Feeds_Ratio::getList(array('where' => '1'));
            if($results && is_array($results)) {
                foreach($results as $result) {
                    self::$_ratio[intval($result['category'])] = intval($result['ratio']) / 100;
                }
            }  
            if(!isset(self::$_ratio) || !is_array(self::$_ratio)) {
                (self::$_debug) ?  Helper_Log::writeApplog('newFeatureOfMixedFeed', "读库失败的情况下 => 采用默认的配比") : null;
                /** 读库失败的情况下 => 采用默认的配比 **/
                self::$_ratio[self::RATIO_24HOUR] = self::PERCENT_24HOUR;
                self::$_ratio[self::RATIO_HOUR]   = self::PERCENT_HOUR;
                self::$_ratio[self::RATIO_LOCAL]  = self::PERCENT_LOCAL;
                self::$_ratio[self::RATIO_REC]    = self::PERCENT_REC;
                self::$_ratio[self::RATIO_TAG]    = self::PERCENT_TAG;
                self::$_ratio[self::RATIO_UVE]    = self::PERCENT_UVE;
            }
            (self::$_debug) ? Helper_Log::writeApplog('newFeatureOfMixedFeed', "设置配比的Redis缓存数据:" . var_export(array(0 => self::$_ratio), true)) : null;
            /** 设置缓存 **/
            self::$_redisDb->del(Conf_Sailmixfeed::REDIS_CONF_RATIO_KEY, array());
            self::$_redisDb->rPush(Conf_Sailmixfeed::REDIS_CONF_RATIO_KEY, array(), array( 0 => self::$_ratio));

问题:

由业务代码和redis日志可以看出,正常情况下通常会有一次删和一次写之后,会出现大量的读。然而,日志中则出现了大量的写入。这就是高并发时导致同时写入后,也许因为写入量特大,导致下次读取失败后继续进行写入,最后把redis拖垮。

 

解决:

  1. 如果不需要进行排序截取等操作可以换用Memcached缓存;
  2. 在高并发情况下应对redis加事务机制:

 

redis事务:

            $redis_db->watch(Conf_Sailmixfeed2::REDIS_CONF_KEY, $args);
            $redis_db->multi();
            $redis_db->del(Conf_Sailmixfeed2::REDIS_CONF_KEY, $args);
            $ss         = $redis_db->rPush(Conf_Sailmixfeed2::REDIS_CONF_KEY, $args, $mix_data);  
            $redis_db->exec();

 

修改后的业务代码:

        /** 直接读静态变量 **/
        if(isset(self::$_ratio) && !empty(self::$_ratio)) return self::$_ratio;
        
        /** 初始化数据 **/
        $mc = Comm_Mc::init(Comm_Mc::BASIC);
        $mc->setUseL0(false);
        self::log("初始化配比数据.");
        
        /** 判断是否有缓存数据 **/
        $results            = $mc->getData(self::REDIS_CONF_RATIO_KEY);
        if($results) {
            /** 有缓存的情况 **/
            self::$_ratio   = $results;
            self::log("有缓存的情况:" . var_export(self::$_ratio, true));
        } else {
            /** 无缓存的情况 **/
            
            /** 读库 **/
            $_results = ModelMixed_Feeds_Ratio::getList(array('where' => '1'));
            if($_results && is_array($_results)) {
                foreach($_results as $_result) {
                    self::$_ratio[intval($_result['category'])] = intval($_result['ratio']) / 100;
                }
            }
            
            /** 读库失败的情况 **/
            if( count(self::$_ratio) == 0) {
                /** 读库失败的情况下 => 采用默认的配比 **/
                self::$_ratio[self::RATIO_24HOUR] = self::PERCENT_24HOUR;
                self::$_ratio[self::RATIO_HOUR]   = self::PERCENT_HOUR;
                self::$_ratio[self::RATIO_LOCAL]  = self::PERCENT_LOCAL;
                self::$_ratio[self::RATIO_REC]    = self::PERCENT_REC;
                self::$_ratio[self::RATIO_TAG]    = self::PERCENT_TAG;
                self::$_ratio[self::RATIO_UVE]    = self::PERCENT_UVE;
                self::log('newFeatureOfMixedFeed', "读库失败的情况下 => 采用默认的配比");
            }
            
            $mc->setData(self::REDIS_CONF_RATIO_KEY, array(), self::$_ratio);
            self::log("无缓存的情况: " . var_export(self::$_ratio, true));
        }
        return self::$_ratio;

注:封面配图为朋友圈图片


文章来源:胡旭个人博客 => 【原】高并发场景下的缓存使用误区

参考文章:

转载请注明出处,违者必究


这是一篇原创文章,如果您觉得有价值,可以通过捐赠来支持我的创作~
捐赠者会展示在博客的某个页面,钱将会用在有价值的地方,思考中...


分类: Memcached, PHP, Redis, 技术, 编程 | 标签: , , , , , , , | 评论 | Permalink

发表评论

电子邮件地址不会被公开。