凌晨两点告警接口响应时间从 50ms 涨到 2000ms。这种时候最怕的是什么不是问题复杂是乱敲命令——top 看一眼 CPU 不高就去重启服务重启完好了十分钟又卡了。一晚上循环了六次。这篇把我这几年排障的流程完整写一遍。核心是先建立方法论再谈命令。先记住两条法则第一条分层。所有性能问题最终都能归到这几层里层关注资源典型症状CPU核心、调度器%usr/%sys 高、上下文切换高内存RAM、swap、page cacheswap 使用上涨、kswapd 占 CPU、触发 OOM磁盘 I/O磁盘、队列iowait 高、await 长、队列堆积网络网卡、TCP 栈重传、丢包、softirq 高应用线程、锁、系统调用系统指标都正常但延迟就是高第二条USE 法则。对每一层资源检查三个维度Utilization使用率资源忙的时间占比Saturation饱和度有多少活排着队等Errors错误数出错的次数很多人只看第一个。但饱和度往往才是真相——磁盘使用率只有 60%可队列里堆了几十个请求实际延迟照样爆。第一分钟四条命令不管什么告警先跑这四条三十秒内能定位到大致方向uptimevmstat110iostat-xz15ss-suptime看负载。load average: 12.50, 8.30, 4.10关键负载要和核数对比。8 核机器负载 12 就是超载了64 核机器负载 12 完全正常。nproc看核数。还有个陷阱负载高但 CPU 使用率低说明有大量进程卡在 D 状态不可中断睡眠通常是在等磁盘 I/O。这种情况加 CPU 没用得查磁盘。vmstat 1 10是信息密度最高的一条命令。procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 8 4 0 512000 80000 6000000 0 0 8000 12000 20000 45000 25 15 45 15 0重点看四组r等待运行的进程数。持续大于核数说明 CPU 不够。b不可中断睡眠的进程数。大于 0 且持续说明 I/O 有阻塞。si/soswap 换入换出。只要持续非 0 就是警报说明物理内存不够了性能会断崖式下跌。us/sy/id/wa用户态、内核态、空闲、I/O 等待。wa超过 5% 就要查磁盘sy很高比如超过 30%往往意味着系统调用太频繁或者有锁竞争。iostat -xz 1 5看磁盘。Device r/s w/s rkB/s wkB/s await %util aqu-sz sda 1200 800 48000 32000 18.5 98.2 12.3%util设备忙的时间比。超过 80% 基本就是打满了SSD 可以放宽到 95%。await平均 I/O 等待时间毫秒。机械盘一般几毫秒SSD 一般 0.1 到 1ms。固态盘 await 上到 10ms 以上基本说明饱和。aqu-sz平均队列长度。这个就是饱和度大于 1 且持续增长说明设备跟不上请求。rkB/s / wkB/s实际吞吐。可以和服务器的磁盘标称性能对比。ss -s看连接。TCP: 1520 (estab 1400, closed 80, orphaned 0, timewait 30)established 数量异常高可能是连接没复用、或者有慢查询把连接占住了。timewait 特别多说明短连接太频繁。这个数要和后端的连接池、以及 ulimit 对一下。第二到五分钟按方向深挖第一分钟能确定卡在哪层接下来就是找具体是谁。如果瓶颈在 CPU# 每个核的使用情况看是不是单核跑满mpstat-PALL15# 每个进程的 CPUpidstat-u15# 找热点函数perftop-pPIDmpstat -P ALL有一个经典用途发现单线程瓶颈。整体 CPU 只有 12%8 核里 1 核跑满看起来一切正常实际上那个核已经到极限了。Java 服务的话从进程定位到具体线程# 找出最占 CPU 的线程top-HpPID-bn1|head-20# 把线程 ID 转成十六进制printf%x\n12378# 在 jstack 输出里找这个线程jstackPID/tmp/jstack.txtgrep-A30nid0x305a/tmp/jstack.txt这套组合能直接看出是哪段代码在烧 CPU比看监控图表有用得多。想看得更直观就出火焰图gitclone https://github.com/brendangregg/FlameGraph.git /opt/FlameGraph perf record-F99-g-pPID--sleep30perf script|/opt/FlameGraph/stackcollapse-perf.pl|/opt/FlameGraph/flamegraph.plcpu.svg-F 99是每秒采样 99 次避开 100 这种整数减少和定时任务撞拍的概率。如果瓶颈在内存free-hcat/proc/meminfo|grep-EMemAvailable|Dirty|Slab# 看每个进程更准确的内存PSS 比 RSS 准smem-rkt-spss# 查有没有被 OOM 杀过dmesg|grep-iout of memory-A20这里有个必须纠正的认知free那一列低不代表内存不足。Linux 会把空闲内存拿去做 page cache 加速文件读写所以free显示很小是正常的。真正该看的是availabletotal used free shared buff/cache available Mem: 31Gi 18Gi 800Mi 1.0Gi 12Gi 12Gi这里的available才是新程序能用多少内存。如果按(total - free) / total来算使用率你会得到 97% 的假告警——很多监控系统就是这么错的然后运维半夜被叫起来看一个根本没问题的机器。写告警用这个表达式(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 90另外dmesg里如果能看到 OOM Killer 的日志说明已经真出事了——有进程被杀过要找出来它为什么吃这么多内存。注意 OOM Killer 是按oom_score挑牺牲品的分数高的先被杀通常是那个吃内存最多的大进程但也可能是被人为调过优先级的。如果瓶颈在磁盘 I/Oiotop-oP-b-n5-d1# 哪个进程在抢 IOpidstat-d15# 每进程的读写速率blktrace-d/dev/sda-otr-w10# 块层级别的追踪想精确定位延迟分布# I/O 完成时间的直方图bpftrace-etracepoint:block:block_rq_complete { latency hist(nsecs); }# 看看谁在疯狂发 IO 请求bpftrace-etracepoint:block:block_rq_issue { [comm] count(); }bpftrace需要内核 4.9 以上基本都满足。这类 eBPF 工具的好处是开销极低可以放心在生产上用——比strace安全得多。如果瓶颈在网络ss-s# 连接状态汇总nstat-az|grep-i-Edrop|retrans# 丢包和重传ethtool-Seth0|grep-i-Edrop|errorsar-nDEV15# 网卡吞吐sar-nETCP15# TCP 重传统计TCP 重传率是最值得盯的网络指标。重传率超过 1% 就该查了超过 5% 说明链路有明显问题或者有丢包策略在起作用。如果系统指标都正常但延迟就是高这一层最容易被忽略但这才是排查真正的终点。CPU 不高、内存够用、磁盘不忙、网络正常——那问题一定在应用内部锁竞争、连接池太小、GC 停顿、或者某个依赖在慢慢响应。# 看系统调用耗时分布strace-c-pPID# 采样看看时间花在哪perf record-g-pPID--sleep30perf reportstrace -c会输出每个系统调用的次数和总耗时。如果看到futex调用次数特别多、耗时特别长基本就是锁竞争。epoll_wait的耗时占比很高反而正常那是事件循环在等。还有一种情况Java 服务的 GC 停顿。用jstat -gcutil PID 1000看如果 Full GC 频繁、单次超过 1 秒2000ms 的毛刺就能解释通了。把流程串起来看回到开头的那个事故。实际排查过程是这样的Step 1 uptime → 负载 24机器 8 核超载 3 倍 Step 2 vmstat 1 10 → r6有 CPU 排队b12大量 D 状态wa22% Step 3 iostat -xz 1 5 → sda 的 %util 100%await 210msaqu-sz 35到这里已经很清楚了CPU 不是瓶颈磁盘是。而且不是普通的高负载磁盘是严重饱和。Step 4 iotop -oP → 一个日志写入进程占了 90% 的写 IO Step 5 查这个进程 → 日志轮转没配单文件已经 40GB根因出来了日志文件没做轮转写日志时文件系统的元数据操作越来越慢把磁盘 IO 全占了其他所有进程的读请求全排在最长的队列后面。解决办法不是加磁盘是配 logrotate。这就是分层排查的价值——如果你一开始盯着 CPU 看会得出CPU 不高啊的结论然后去重启服务。常用命令速查按先看什么、再看什么的顺序想看什么命令负载和核数对比uptime/nproc系统总览vmstat 1 10每核 CPUmpstat -P ALL 1 5每进程 CPUpidstat -u 1 5磁盘iostat -xz 1 5磁盘按进程iotop -oP内存free -h看 available内存按进程smem -rkt -s pss连接ss -s网络重传nstat -az | grep -i retrans热点函数perf top -p PID系统调用strace -c -p PID历史数据sar -u 1 5/sar -d/sar -n DEVsar特别值得单独提一句它是唯一能回看历史的工具。前面那些都只能看现在但事故往往是凌晨三点发生的你早上才被叫起来。sar配合 sysstat 的后台采集能告诉你当时每一层是什么状态。sar-u-f/var/log/sysstat/sa25# 看 25 号的 CPU 历史sar-d-f/var/log/sysstat/sa25最后工具是死的判断是活的。我总结下来最有用的一条经验是不要凭直觉跳过任何一层。大多数排障失败不是因为命令不够是因为跳过了某层的验证就下了结论——“CPU 不高那肯定是网络问题”然后查了两小时网络最后发现是磁盘。先按分层跑一遍每层都用数据确认或排除剩下的那层就是答案。慢一点但不会走回头路。另外提醒一句别在生产上做压测。压测要在隔离环境做有监控、有负责人、有回滚预案。生产上的压测只会把小问题变成大事故。