线上Redis流量优化经历全过程,一点点的小改动,流量下降500%

简要说明:

1、阿里云Redis服务。
2、32G内存,100M带宽。
3、Redis的Get请求频率大约5000次/s。
4、Redis的Key总量约2000万。

原因:

高峰期已过,阿里云钉钉群每天至少有一次流量报警,有时更多。

处理方法:

1、阿里云离线全量Key分析数据。

1)第一步分析大Key,大部分是集合,找代码没有发现取全量。
2)第二步按KEY占用大小倒序,找到部分频率高,且KEY值较大键值优化。主要是减小本身key值的大小。(优化后没有效果,有些时候可以解决问题)

2、通过monitor实时命令分析。

1)第一步把Redis在5秒内执行的GET打印到文件(时间再长就有点多),进行筛选,没有找到原因,执行命令如下。

redis-cli  -h 主机名 -p 6379 -a 密码 monitor |grep "GET" > /tmp/temp-1.log

线上Redis流量优化经历全过程,一次小小的改动,流量下降500%

2)第二步把导出数据放在Excel分析,抽取数据中某一秒的所有数据(要取中间, 不要取最前面和最后面的),肉眼寻找可能有问题的KEY进行优化。(问题仍未解决)
3)第三步在分析第二步时,发现有些Key已过期,Get不到数据。
4)第四步写一个直接分析monitor日志脚本,并打印所有Key大小。

 /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $file = "/tmp/monitor-2.log";
        $totalLen = $this->fileHandel($file);
        echo "完成 总大小:"  . $totalLen . "\r\n";
        return 0;
    }

    /**
     * 根据文件获取
     */
    public function fileHandel($fileURL)
    {
        $handle = fopen($fileURL, "r");
        $totalLen  = 0;
        if ($handle) {
            while (($line = fgets($handle)) !== false) {
                // 处理每一行
                preg_match('/\[([0-9]+)[\s\S]+GET[\s\S]+"([^"]+)"/i', $line, $arr);
                if (count($arr) != 3) {
                    echo "错误";
                } else {
                    $len = $this->getLen($arr[2], $arr[1]);
                    $totalLen += $len;
                    echo $len . "  " . $arr[2] . "    " . $arr[1] . "\r\n";
                }
            }
            fclose($handle);
        } else {
            echo "无法打开文件";
        }

        return $totalLen;
    }

    private function getLen($key, $dbSelect)
    {
        //去掉前缀
        $key1 = (string)str_replace("redis端缀", "", $key);

        //设置库名
        Redis::select($dbSelect);

        //查询大小
        $len = Redis::strlen($key1);
        return $len;
    }

线上Redis流量优化经历全过程,一次小小的改动,流量下降500%

5)第五步根据Key大小再筛选,然后再看看1秒内执行的次数,最终发现了问题。
线上Redis流量优化经历全过程,一次小小的改动,流量下降500%
6)最后优化代码

        // 新代码
        $key = \getRedisKey('qSubjectList') . implode("", $ids);
        $fields = [  'id', 'name' ];
        $list =  Cache::remember($key, TTL::TTL_DAY_RAND(), function () use ($fields, $ids) {
            return self::query()->where('status', self::STATUS_ON)
            ->select($fields)
            ->whereIn("id", $ids)
            ->get()->toArray();
        }) ?? [];
        return collect($list)->whereIn('id', $ids);

        // 旧代码,需要优化
        // $list = self::getCacheList();
        // return collect($list)->whereIn('id', $ids);

7)更改代码后,实际效果如图:
线上Redis流量优化经历全过程,一次小小的改动,流量下降500%

总结:

1、最难的问题是怎么找到这个Key,为了找这个Key花费了不少时间。
2、最主要问题还是代码质量,所以大家写代码时一定按需取字段,且记不可以把所有数据存在一个Key,而是按唯一性存储(后台有时候方便,可以使用)。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 11

这样写每次$ids不一样都会查询数据库啊

我一般做法key是 getRedisKey('qSubjectList') . $id
1、通过 pipeline 循环获取命中缓冲的
2、db查询 未命中缓冲的,然后写入缓冲
3、最后合并数据

1个月前 评论
huangYX (楼主) 1个月前
huangYX (楼主) 1个月前
雷雷 (作者) 1个月前

排查:访问频繁且值较大的 key(流量最大)
措施:原来 qSubjectList 包含了所有 id 的数据,查询时是取出来再筛选 ids 比如 [1,2,3] 的数据,现在是将 ids [1,2,3] 单独存储到一个键值。
理解的对吗?

1个月前 评论
huangYX (楼主) 1个月前
php_yt (作者) 1个月前

感觉写得稀里糊涂的,没看明白?有大能帮忙解释下吗?

3周前 评论
huangYX (楼主) 3周前
php_yt 3周前
老年人 (作者) 3周前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!