系统突然变慢,到底慢在哪?从入口到代码行的追踪实战

昨天一个客户发消息:“订单接口突然慢了,P99从100ms飙到3秒。你看是哪里的问题?”
我回他:“有没有Trace ID?”
“有,我发你。”
打开调用链,一眼看到订单服务调库存服务花了2.8秒。再点进库存服务,发现里面调了Redis,耗时2.7秒。再看Redis命令,是一个HGETALL,扫了1万个字段。
问题找到了:不是代码逻辑慢,是Redis里一个Hash存了太多字段,HGETALL扫了全量。拆成多个Key后,查询降到50ms。
这是慢调用排查的标准路径:从用户请求,一路追到代码行。
今天聊聊系统变慢时,怎么从入口到代码行,一步步找到真凶。
01 确认范围:是全局慢还是局部慢?
别上来就查代码。先搞清楚范围。
谁觉得慢?
所有用户都慢 → 可能是系统级问题(数据库、网络、资源耗尽)
只有某个地区慢 → 可能是CDN、专线、运营商问题
只有某个用户慢 → 可能是用户数据量大、账号特殊
哪个接口慢?
所有接口都慢 → 基础设施问题(CPU、内存、IO、网络)
只有个别接口慢 → 那个接口的依赖或代码有问题
什么时候开始慢?
刚发完版 → 回滚或查变更
流量高峰时 → 资源不足、连接池满、队列堆积
凌晨也慢 → 定时任务、备份任务干扰
那家客户的慢,是“订单接口”慢,其他接口正常。说明不是基础设施问题,聚焦订单接口。
02 Trace ID:串起所有证据的线
没有Trace ID,日志是散的,调用链是断的,排查靠猜。
Trace ID贯穿请求全链路:从网关→订单服务→库存服务→Redis→数据库→返回。每跳都有Trace ID,每行日志都打Trace ID。
有了Trace ID,你可以:
在APM里看到调用链耗时分布(哪一步最慢)
在日志平台里搜这个Trace ID,看到所有服务打印的日志
在数据库里关联慢查询
那家客户就是靠Trace ID,快速定位到库存服务慢,再进到库存服务的日志,看到Redis命令耗时。
03 分层排查:从上到下,从外到内
拿到Trace ID后,按层剥开。
第一层:网关/负载均衡
看请求到了没?响应时间多久?是不是网关层就慢了?
第二层:应用服务
看调用链,哪个服务耗时最长。订单服务总耗时3秒,其中调用库存服务2.8秒,说明瓶颈在下游。库存服务总耗时2.8秒,其中Redis操作2.7秒,说明瓶颈在Redis。
第三层:数据层
看具体是什么操作慢。是HGETALL扫大Key?还是慢SQL没有索引?还是连接池满了在等?
第四层:基础设施
如果数据层也看不出问题,看CPU、内存、磁盘IO、网络。是不是宿主机争抢?磁盘IO满了?
那家客户的排查路径:订单接口慢 → 调用链看到库存服务 → 库存服务看到Redis → Redis看到HGETALL扫大Key。四层定位完成。
04 代码级定位:火焰图与线程堆栈
有时候调用链只能定位到“哪个服务慢”,但不知道为什么慢。这时候需要代码级工具。
火焰图:看CPU耗时分布。哪块代码占用的CPU时间最多,火焰图上一眼看到。
async-profiler:Java火焰图工具
Pyroscope:持续剖析,可回溯历史
线程堆栈:看线程在等什么。多次打印线程堆栈,看哪些线程一直处于RUNNABLE或BLOCKED状态。
jstack:打印Java线程堆栈在线工具:Arthas
那家客户如果只看调用链,只知道“Redis操作慢”。但不知道为什么慢。火焰图显示HGETALL占用了大量CPU,因为扫描了1万字段。没有火焰图,可能误以为是网络问题。
05 数据层排查:慢查询与连接池
数据层是慢的重灾区。
数据库慢查询:
开启慢查询日志,设阈值(如1秒)
分析SQL:
EXPLAIN看执行计划,缺索引?全表扫描?看锁等待:
SHOW PROCESSLIST看是否有锁阻塞
Redis慢查询:
开启慢查询日志,记录执行超过阈值的命令
看是不是大Key操作(
KEYS、HGETALL、SMEMBERS)看是不是热Key导致单节点CPU高
连接池:
活跃连接数是否接近上限?
获取连接的等待时间是否很长?
那家客户的Redis慢查询日志记录了HGETALL,耗时2.7秒。直接定位到大Key问题。
06 一个真实案例:日志打太多也能慢
一个客户,接口突然变慢。调用链看,业务代码只花了10ms,但总耗时500ms。多出来的490ms去哪了?
排查过程:
看火焰图:大量时间花在日志打印上
看日志配置:生产环境日志级别是DEBUG,每秒写几千条日志
磁盘IO:写日志把磁盘IO打满了
改成INFO级别,接口响应时间从500ms降到50ms。
慢不一定在业务代码,也可能在日志、在框架、在序列化。
写在最后
系统变慢不可怕,可怕的是不知道怎么找。
那家客户的运维负责人后来总结了一个口诀:“先问范围全局还是点,Trace ID串起所有链;调用链上看耗时,哪层最厚哪层查;火焰图里找热点,线程堆栈看等待;慢查询日志不能关,磁盘IO别打满。”
下次用户说“转圈圈”,你知道怎么找到真相了吗?