华为云 DCS 分布式缓存运维实战:从慢日志定位到大 Key 治理的一次完整排查

举报
yd_260132607 发表于 2026/10/30 09:09:41 2026/10/30
【摘要】 上周我们一个线上服务在晚高峰突然开始超时,报警群刷了两屏,但真正定位到原因花了将近三个小时。事后复盘的时候我们发现,问题不在于「查不到」,而在于「查起来太慢」——很多关键信息其实是有的,只是散落在 Redis 实例的不同入口,需要我们一台台登录、一条条命令去捞。这篇就把我现在的一套排查方法完整写下来,核心是三个动作:把「慢」量化、把「慢」和「数据结构」关联起来、再把每次排查的结论固化成可重复...

上周我们一个线上服务在晚高峰突然开始超时,报警群刷了两屏,但真正定位到原因花了将近三个小时。事后复盘的时候我们发现,问题不在于「查不到」,而在于「查起来太慢」——很多关键信息其实是有的,只是散落在 Redis 实例的不同入口,需要我们一台台登录、一条条命令去捞。

这篇就把我现在的一套排查方法完整写下来,核心是三个动作:把「慢」量化、把「慢」和「数据结构」关联起来、再把每次排查的结论固化成可重复执行的巡检脚本。

一、先把「慢」量化,而不是凭感觉

我们以前判断 Redis 慢,靠的是业务方一句「好像有点卡」。这个描述没法定位,也没法复盘。后来我在服务里加了两个东西:一个是接口耗时的分位统计(P99 和 P999),另一个是 Redis 命令的调用耗时分布,两个都打在同一条日志链路上,带同一个 traceId。

这一步的价值在于把「慢」变成了一个可以跟时间对齐的序列。有 P99 曲线之后,我们才看清楚,那次超时不是一直慢,而是从某个具体时间点开始突然劣化,同时命令耗时分布里某个特定类型的命令尖刺式上去了。如果没有这条曲线,我们只能在日志里人工翻,那三个小时有一半花在这上面。

这里有个细节:一定要统计命令耗时的分布,而不是平均值。平均值会把一个耗时 2 毫秒的 GET 和一个耗时 2 秒的 GET 混在一起,看着都还行,其实问题就藏在那 2 秒里。

二、慢日志是把范围缩小的第一把刀

打开慢日志之后,第一件事是定阈值,不要一上来就设成 10 毫秒。我的做法是先设一个相对宽松的值(比如 50 毫秒),跑半天,攒够样本再看分布,然后再把阈值收到你关心的区间。因为阈值太低会让慢日志文件巨大,反而不好分析。

看慢日志的时候,不要一行一行读,先做聚合。我会把慢日志按「命令名 + 参数模式」分组看,比如把 KEYS 归一类、SCAN 归一类、HGETALL 归一类。这一步通常就能看出问题:某个命令占了慢查询总数的 80%,那方向就明确了。

参数模式这里有个小技巧值得说。如果一批慢日志都是 HGETALL 同一个 hash 的同一个 field,那大概率是字段太大;如果都是 SCAN 那就是游标推进的问题;如果命令名本身五花八门但耗时都在几十毫秒,那更可能是网络或者实例负载到了阈值,而不是某条命令本身有问题。命令分布均匀但整体慢,通常要往实例层面去看,不是往 key 层面看。

三、慢命令只是表象,往下挖一层是数据结构

我们那次事故的根因是一个被误用的数据结构。有个同事为了「一次拿全」方便,把一个上百万字段的 hash 塞进了 Redis,然后用 HGETALL 整块拉出来。这在开发环境完全没问题,数据量小的时候甚至更快,但在流量上来之后,它就成了一个典型的「单次拉取的数据量随业务增长而无限膨胀」的设计缺陷。

复盘之后我给自己定了一条判断原则:在 Redis 里,任何会一次返回全量数据的命令,都要先问一句「这个数据量会不会随时间增长」。答案是会的话,就不能这么用。

治理手段无非几种:能改代码的,直接改成用 HSCAN 分批取;不能改代码的,就把数据结构拆开,按时间或者按某个维度切片,让单次访问的量有上界。拆完之后再配合缓存预热,延迟会稳定很多。

另一个容易被忽略的是大 key 的存在本身。大 key 不一定会慢,但它会让很多看起来正常的操作变慢:主从同步、网络带宽、以及阻塞式删除时的长时间停顿。我们后来加了一个大 key 扫描巡检,把超过阈值的 key 列出来逐个处理,效果比事后救火好很多。

四、热 key 和大 key 是两件事

刚开始的时候我们把热 key 和大 key 混为一谈,后来发现这是两个不同的问题,治理手段也完全不同。

大 key 是「一个 key 太大了」,热 key 是「有一个 key 被访问得太多了」。热 key 的典型表现是某个命令的耗时尖刺集中在少数几个 key 上,其他 key 都正常。这种情况单靠优化数据结构解决不了,因为问题在访问分布上。

我们处理热 key 的思路是打散:把一个热 key 的内容按某个维度拆成多个子 key,让请求按 hash 取模分散到不同子 key 上;同时在客户端做一层短 TTL 的本地缓存,把最热的那部分挡在 Redis 之前。做完之后单实例的 QPS 峰值明显降下来了,而且这个改造对业务代码几乎透明。

五、把排查经验固化成巡检脚本

最后是这次事故最大的收获:不要让排查经验只存在于某个人的脑子里。

我现在维护一个巡检脚本,每天定时跑一次,输出几件事:当前大 key 列表、最近的慢命令 TOP、以及连接数和内存使用率的变化趋势。这个脚本本身不复杂,但它把「出问题的时候才去查」变成了「每天都知道」。更关键的是,它有历史对比——同一个指标连续几周都在往上走,通常意味着数据在缓慢膨胀,这类问题在告警触发之前就能看出来。

我们还把这个脚本接到了 CI 里,配一份 JSON 格式的阈值配置。改配置不用改脚本,脚本跑不跑也由定时任务控制。现在新人接手的时候不需要知道任何历史经验,只要看巡检报告就能知道当前的状态。

回过头看,那三个小时里真正花掉时间的不是定位,是「把信息从各个入口捞出来拼在一起」。把这件事变成一个每天自动跑的脚本,比记住任何一条命令都有用得多。

如果你也在用 Redis 做缓存,我最想建议的一句是:先学会读慢日志的聚合分布,再去看具体命令。你会发现大部分「偶发卡顿」其实是某个固定结构在持续放大,只是平时被平均数掩盖了。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。