排查性能问题这几年,我越来越觉得,真正值钱的本事不是背命令,而是知道什么场景该看什么指标,指标背后的含义又是什么。Linux 常用的性能分析工具,说多不多,说少不少,装一整套 sysstat 全家桶其实也就那么几个命令,但很多人卡在第一步:不知道从哪看起,或者被一堆数字绕晕。这篇文章我想把日常排查中最常用的工具串起来,从整体思路到具体指标,再到实战流程,把每个工具解决什么问题、常见的坑在哪里说清楚。适合刚接触性能分析的运维和开发,也适合已经有经验但想系统补一下细节的老手。
1. 排查前先想清楚:性能分析的两条基本思路
动手敲命令之前,先花两分钟把思路理清楚。这就好比你去看病,不能一上来就要求拍 CT、做核磁,而是先量体温、听心跳,有个大方向再决定做哪些检查。性能排查也是一样的逻辑。
1.1 自顶向下:先从整体看到局部
所谓自顶向下,就是先看整体资源状况,再逐步细化到进程、线程、函数。整体的资源就只有四种:CPU、内存、磁盘 IO、网络。任何一个性能问题,最终几乎都能落到这四类资源上。
我从同事那里学到并一直沿用的方法是把问题拆成三个方面:利用率、饱和度、错误。利用率就是资源被使用的比例,比如 CPU 用到了 80%;饱和度是指资源是否已经“排满队”,比如 CPU 就绪队列特别长,或者磁盘 IO 队列堆积;错误则是系统自己爆出来的异常,比如网卡丢包、磁盘 IO 错误、应用日志里的超时。
这三个维度基本覆盖了排查的全部起点。利用率太高,要看谁在用;利用率不高但响应慢,大概率是饱和度问题——队列在排队;有错误,先处理错误,性能数据就算再漂亮也没意义。
1.2 工具地图:我常用的工具箱
针对上述四个方面,我把常用的 Linux 常用性能分析工具分成几个层级,平时排查时心里有张图:
| 资源类型 | 整体指标 | 定位工具 |
|---|---|---|
| CPU/负载 | vmstat、top、uptime | mpstat、pidstat、perf |
| 内存 | free、vmstat | slabtop、smem、dmesg |
| 磁盘 IO | iostat、sar | iotop、pidstat -d |
| 网络 | ss、sar -n | iftop、tcpdump、nicstat |
| 通用追踪 | strace、perf、dstat | gdb(最无奈时才用) |
这套组合里,优先看的是 vmstat、iostat、free、ss 这四类,因为它们的开销极低,适合在生产环境直接跑。perf 和 strace 这类动态追踪工具开销相对高,一般放在定位阶段用。dstat 是个很好的全能型命令,能一个屏幕同时展示 CPU、磁盘、网络的实时变化,不过它不在 sysstat 包里,很多机器默认没有,要用的话得提前装。
1.3 性能分析最忌讳的一件事:只看单点数据
刚入行时我犯过的错是:接到告警直接跑top看一眼,load average 高就以为 CPU 不够,再瞄一眼 wa 很高就以为是机器磁盘坏了。其实单看一次 top 的输出远远不够,指标它是会抖动的。
我现在每次上机器,先抓一两条命令连续好几秒的输出,比如vmstat 1 5,就是每隔一秒采集一次,一共采五次。为什么要这样?因为有些资源问题一会儿就过去了,单次快照根本看不出来。就好比你看一个人饿了没,你得观察他一整天的饭量,而不是看某一分钟的肚子叫声。
所以排查的第一步永远是:先确定时间线。问题是持续性的,还是周期性出现,还是刚发生完就恢复?确认后再选工具,事半功倍。
2. CPU 与系统负载:一组命令看清“忙在哪”
CPU 是排查时最先看的资源,因为它的指标最直观,但误区也最多。
2.1 top:别只盯着 load average,先看 us/sy/wa/id
top是几乎所有 Linux 工程师敲下的第一个性能命令。大家通常关注第一行的 load average,其实最值得看的反而是第二行的几个百分比:
- us:用户态 CPU 使用率,跑应用程序代码基本消耗在这里;
- sy:内核态 CPU 使用率,系统调用、内核线程、中断处理都在这里;
- wa:CPU 等待 IO 完成的时间占比,这个值高通常意味着磁盘或网络 IO 是瓶颈;
- id:空闲率,看着像没用,其实隐藏信息很丰富;
- st:被虚拟机管理程序偷走的时间。如果你用的是云主机,st 高往往说明宿主机上邻居在抢 CPU,你自己调应用没用,得换规格或者换宿主机。
举个例子。有一次一个数据库集群出现响应变慢,top里 wa 一直在 30% 以上,us 反而不高。按照当时的情况,第一反应是 CPU 不够是不是该扩容了。但仔细一看 wa 占这么高,说明问题的根子在磁盘 IO,给 CPU 扩容没有意义,只有压掉 IO 这头的延迟才能解决。
值得注意的是,top默认的刷新间隔是 3 秒,如果你需要看更细的变化,进 top 后按d可以改间隔,也可以按1展开每个逻辑核的情况。多核 CPU 不是铁板一块,有时候整体 us 只有 30%,但某个核已经 100%,应用是单线程的话,瓶颈就出在那个核上。
2.2 load average 到底该怎么理解
uptime输出的 load average 有 1 分钟、5 分钟、15 分钟三个值。很多人以为它和 CPU 使用率是一回事,这是一个很大的误解。
load average 统计的是系统中处于可运行状态和不可中断睡眠状态的进程平均数量。可以理解为“CPU 门口排队的队伍长度”,包括正在跑的进程和正在等待的进程。如果这个数值长期大于 CPU 逻辑核数,说明已经有人排队了;如果你只有 2 核,load average 到了 8,队列里有 6 个进程在等 CPU,可以想见调度会有多紧张。
但 load average 高不一定是 CPU 不够。因为不可中断睡眠状态的进程也包括等待磁盘 IO 的进程,所以磁盘慢同样会让 load 飙升。有一次生产环境 load 报警到了 30,CPU 利用率却不足 20%,排查到最后发现是磁盘阵列的故障导致的 IO 卡顿,所有进程都排在了磁盘 IO 上,而不是 CPU 上。
读这三个数也有技巧:1 分钟远大于 15 分钟,说明负载是刚刚升上来的,问题可能在持续;反过来,1 分钟比 15 分钟低很多,说明峰值已经过去了。不要看到 load 高就慌,得先搞清楚是谁在排队。
2.3 mpstat 与 pidstat:从全局到进程级
top能看到整体和各核概况,但想进一步知道“是哪个进程在消耗 CPU”,还是得用pidstat。这个命令属于 sysstat 包,按进程维度输出 CPU 使用率,还能按线程维度看。
比如说,pidstat -u -p PID 1可以每秒稳定输出某个进程的 CPU 占用。排查应用 CPU 飙升时,我最常用的写法是:
pidstat -u -p 12345 1 10输出里会有 %usr、%system、%guest、%CPU 等列,但更能帮到我们的是,如果你发现某个进程的 %system 很高,说明这个进程在内核态耗费了大量 CPU,比如频繁的系统调用、频繁的内存分配、大量的文件读写。此时就可以顺着线索用strace去追踪了。
mpstat则是看每个 CPU 核的使用细节,比如mpstat -P ALL 1每个核的 us、sy、irq、soft 等都会分列展示。它特别适合排查中断不均的情况。네트워크密集型应用有时候所有中断都打在一个核上,整体看起来 CPU 使用率只有 25%,实际那个核已经 100%,处理能力卡住不动了。用mpstat -P ALL就能一眼看出来。
2.4 上下文切换与中断:性能隐形杀手
有一类性能问题,CPU 利用率看着不高,但应用就是慢。这种多半是上下文切换过于频繁或中断分布不均导致的。
vmstat输出的cs列就表示每秒上下文切换次数。我通常用pidstat -w来看进程的主动切换和被动切换:
pidstat -w 1输出里的 cswch 是主动切换,nvcswch 是被动切换。被动切换高往往意味着线程数量过多、锁竞争严重。典型的例子是一个多线程应用开了几百个线程,但锁设计不好,线程都在互相等待,CPU 空转但切换量惊人。这时候加 CPU 没有用,卸载线程池和锁结构才是正解。
我还遇到过一种情况,大量小文件的读写导致频繁的系统调用和 IO 中断,中断全挤在一个核上,那个核的 soft 指标能到 90% 以上。定位这类问题mpstat -P ALL、cat /proc/interrupts就够了。机器上有多个网卡队列的,还可以通过调整中断亲和性(把中断绑定到不同核)缓解单核压力,这个属于系统调优的范畴,前提是你先能看得出问题分布。
3. 内存与 swap:free 和 vmstat 的细读方法
内存问题的排查,难不在看总用量,而在分清内存到底是被谁拿着,拿得合不合理。
3.1 分清 buff 和 cache,以及 free 里的 used 为什么这么高
很多新手看到free -h里 used 很高、free 很低就紧张。先别急着杀进程,关键的是看 buff/cache 这两列。
buff 是块设备读写时用的缓冲,cache 则主要是页缓存——读过的文件内容会留在 page cache 里,下次读就直接命中内存,这是内核正常的加速行为。这部分内存其实是可回收的,当应用程序需要更多内存时,内核会自动把这些缓存释放掉。所以 used 高不一定代表内存不足,得看实际的应用可用内存。
我常用的判断命令是:
free -h重点看 available 这一列。available是内核自己估算的“还能给新程序分配多少内存”的数字,它已经把可回收的 cache 部分考虑进去了。如果 available 长期偏低,比如只有总内存的 5% 以内,才真正有内存压力。如果只是 used 高而 available 充足,不用去折腾。
如果要看 page cache 具体占了多少,可以用cat /proc/meminfo里的 Cached 和 SReclaimable 两项,这两个加起来基本就是可回收的缓存部分。在 Linux 上看到 Slab 这一项也别怕,它一般是内核对象的缓存,比如 inode、dentry 等,同样大部分可回收。
3.2 vmstat 里的 si/so 与 page fault
内存问题的核心命令我首选vmstat 1,它每次输出一行,信息密度很高:
- r:运行队列中的进程数,和 CPU 排队相关;
- b:阻塞队列中的进程数,通常在等 IO;
- si、so:从 swap 换入、换出内存的速率,单位是 KB/s;
- us、sy、id、wa:CPU 各类时间占比;
- cs:上下文切换次数。
si、so 如果持续大于 0,说明系统正在把内存换到磁盘上又来回来去。我见过一个系统大量打日志,内存紧张被 OOM 之前疯狂 swap,进程都变得莫名其妙地“卡顿”,但 CPU 和磁盘利用率都不算高。最终发现根源是一个内存泄漏问题,不是磁盘也不是 CPU。
但是内存问题还有个隐蔽指标——page fault。它分两种:minor page fault 只是缺页后从缓存里找到页,开销相对小;major page fault 则是真的会去读磁盘,一次动辄几百微秒甚至更久。对应用影响大的其实是后者,可以用pidstat -r查看进程的 minflt/s 和 majflt/s。如果 majflt 一直有值,就得警惕内存不够导致频繁在磁盘上换页。
3.3 OOM 排查:别只看 dmesg
内存耗尽触发的 OOM killer,是排查内存问题时必须面对的场景。系统 OOM 后在dmesg里会有明确的记录,标出进程名字和内存占用,这是第一手证据。
我的排查习惯分三步:
第一,dmesg | grep -i oom看有没有 OOM 记录;第二,看残留的日志或监控里当时的内存曲线,确认是突然飙高还是持续上涨;第三,如果是持续上涨,八成是内存泄漏,接下来就是拿着进程的 heap profile 去分析了。
有一点要注意:dmesg输出会被新日志覆盖,一旦 OOM 后机器继续运行大量任务,证据可能被冲掉。所以我建议排查期开启内核日志持久化,或者用sysstat定时采集内存数据做历史回放,这样连发生了什么、什么时候开始的都能复盘出来。这也是后面专门讲 sar 的原因。
4. 磁盘 IO:iostat 和 iotop 的实战配合
磁盘 IO 的问题是四类资源里最容易被误判的一类,因为 IO 指标本身的理解门槛高。
4.1 iostat:util 100% 不一定就是满了
iostat -x 1是最常用的磁盘排查命令,最核心的几列是:
- util:设备繁忙时间百分比;
- await:一次 IO 的平均等待时间;
- svctm:IO 实际服务时间;
- aqu-sz:设备平均队列长度;
- r/s、w/s:每秒读写次数。
最大的误区在于:util 接近 100% 不等于设备性能到了极限。比如 SSD 处理大量小数据小块请求时,设备可能一直“忙着”处理,但吞吐量和延迟都处于健康范围;反之机械盘上大量随机读写,util 可能只有 60%,但 await 已经高得离谱,用户体感卡成 PPT。
正确的读法是把 util 和 await、aqu-sz 结合起来看。如果 await 和 aqu-sz 同时在涨,而 util 也高,说明设备确实在过载排队;如果 util 高但 await 低,说明设备忙于大量细小请求,但这些请求响应很快,实际瓶颈可能不在磁盘延迟上。
有一个实际的案例:某文件服务在集中上传时段延迟变大,iostat 显示 util 90%,await 也到几十毫秒,看起来就是磁盘慢。但换成看 vCPU 后发现 CPU 软中断(soft)很高,问题在网卡收包和 IO 栈的上下半部处理上,磁盘本身没问题。后来把网卡多队列和 IO 调度参数调了一下,延迟立马下来了。
4.2 用 iotop 和 pidstat -d 找到“凶手进程”
iostat 告诉我们磁盘“病了”,但要找到“谁打病的”,得靠iotop和pidstat -d。
iotop可以直接看到哪个进程在频繁读写磁盘,单位是 KB/s,还标了 IO 百分比。生产环境要看吞吐最高的几个进程,我常用的设置是:
iotop -o -P-o只显示正在做 IO 的进程,-P显示的是进程而不是线程,这样输出干净不少,不然满屏几十行都是不活动的程序,干扰太大。
pidstat -d则按时间间隔输出进程的读写情况,核心列是 rKB/s、wKB/s,还有 await。它可以统计历史趋势,适合已经知道进程 PID 但想确认它是不是持续在写盘的场景。
4.3 两类容易忽略的“伪磁盘问题”
第一类:日志写得太频繁。应用打日志本身不要紧,但每个业务请求打一行甚至好几行,一旦流量上来,磁盘上全是琐碎的小写,磁盘虽然 capcity 很大,但每秒写入次数就是上不去。遇到这种情况,通常建议合并日志输出或调高日志级别,而不是加盘。
第二类:页面换入换出引起的“伪读写”。内存压力大时,swap 频繁读写,这期间 iostat 里的 w/s 和 await 也会飙升,表象是磁盘忙,根子却是内存不足。之前有一次生产应用突发卡顿,iostat 一看 dev 盘 util 80%,await 明显上扬,第一反应以为磁盘坏了。后来对照vmstat才发现 si/so 非零,内存吃紧导致内核在疯狂换页。先扩容内存,磁盘自然恢复正常。“先看内存再看磁盘”,这个顺序非常重要。
5. 网络与连接:快速判断瓶颈方向
网络层面的排查看似复杂,实际只关心三件事:连接状态正常吗;丢包有没有;延迟和吞吐是否异常。
5.1 ss:比 netstat 更快更全
我现在基本不用netstat,而是直接用ss,因为它在连接数大时不卡,输出也更全。最常用的两个方式:
ss -s ss -tinss -s是一条总览,能看到 TCP 连接、套接字数量等,几秒钟内就能判断机器上是不是有大量非正常连接。ss -tin则是每个连接的信息,重点看Send-Q和Recv-Q。Recv-Q 一直涨说明接收端应用程序处理不过来,数据在套接字缓冲区积压;Send-Q 一直涨则说明本端发送受阻,很可能是对端处理不过来,或者带宽已经饱和。
我遇到过一个典型的案例:业务反馈接口偶发超时,排查应用日志没有异常,ss 却发现大量连接的 Recv-Q 非零——应用进程线程池太小,并发一上来,接收缓冲区积压,内核对端窗口缩小,最终客户端超时。业务侧加线程池后问题消失。
5.2 网卡层指标:errors、dropped 和 softnet
网络问题有时出在网卡和内核处理,而不是应用。ifconfig或ip -s link能看到每个网卡的收发错误和丢包计数,如果 errors 或 dropped 有增长,基本能判断物理链路或驱动层已经出问题了。
但还有一个很容易忽略的系统级计数器:/proc/net/softnet_stat,它的第二列如果一直在涨,表示网络软中断的处理已经跟不上收包速度,网卡中断把 CPU“打死”了。多数时候这是单个核中断集中导致的,调一下网卡多队列或 CPU 亲和性就能缓解。
整体流量占用还有一个实用命令iftop,它可以实时显示各连接占用的带宽。排查“谁在偷偷占带宽”时,iftop -nP就够了,按流量排序一眼看出大流量连接。
5.3 抓包三问:抓什么、怎么过滤、抓多久
到了抓包这一步,说明之前的大方向已经基本定位,需要用数据面证据来验证。我的习惯是先回答三个问题:抓什么接口、过滤什么条件、抓多少秒。
接口选择看流量走向,比如前端服务器就看绑定了业务地址的那块网卡;过滤条件一定先用port 8090或host x.x.x.x这种最粗暴的维度压到不卡,再不断加字段精确;抓包时长一般 10 到 30 秒就够,文件太大反而难分析。
一个比较稳妥的抓包命令示例:
tcpdump -i eth0 -nn -s0 host 192.168.1.100 and tcp port 3306 -w /tmp/trace.pcap-s0抓全帧,-nn不做域名和端口解析,-w落盘再做后续分析,避免终端输出拖慢抓包本身。抓的时候我会看着打印的行数增长速度,如果每秒行数少说明过滤条件有效;如果直接刷屏,就得 Ctrl+C 停止调整条件,硬抓下去只会把机器搞挂。
6. 动态追踪三板斧:strace、perf、tcpdump
静态指标看完了,大部分问题已经能定位到“哪个进程、哪类操作异常”,此时需要用动态追踪工具深入进程内部的现场。
6.1 strace:看系统调用的“慢镜头”
strace跟踪的是进程的系统调用。它适合回答“进程到底在做哪些系统调用、在哪里卡住”。国产的应用日志里看不出来的超时,常常能在 strace 输出里看到亮眼的connect、read或poll长时间不返回。
先看启动耗时场景,最常用:
strace -c -p PID-c是统计模式,结束后汇总各种系统调用的次数和耗时。如果想摆脱每次都手工看海量输出,这就很友好。若需要跟踪某个正在运行的进程,直接strace -p附着上去,但要非常小心:附着后会在进程上产生较大的性能损耗,如果进程本身已经处于“临界过载”状态,有可能让进程直接崩溃或者把故障扩大。
更推荐的做法是:先确认必要,再从短时间开始,比如timeout 10 strace -c -p PID。
6.2 perf:采样到函数级别
如果 CPU 占用集中在某个进程内部,strace只能看到系统调用边界,函数内部的耗时就看不到了。这时用perf做采样。
perf top可以实时显示 CPU 上最耗时的函数,开起来大约一两秒就能看到热点。perf record则是离线采集一段时间再分析。线上判断热点我一般直接:
perf top -p PID它会周期性采样 CPU 的 IP 指针,统计落在哪个函数的时间多。用它抓到过一些“隐形”热点,比如某些程序里大量使用正则匹配累积开销巨大,从业务代码层面根本看不出来,但 perf 采样显示某个匹配函数占比极高。
有一个需要注意的细节:如果目标程序是 JIT 编译的(比如 Java 的 JVM),perf top显示的可能是一堆汇编地址和 JVM 内部符号,不是业务函数,这时要借助 Java 侧的线程 dump 或专门的分析器配合来看,perf 本身帮助有限。
6.3 tcpdump:别在生产环境“裸奔”
前面网络部分已经讲过选包策略,这里补充一个真正能帮到人的细节:抓到包后,第一步先用tcpdump -r trace.pcap -nn | head看一眼能不能读懂,大部分时候连接建立了但没数据——这已经是一个极其有用的结论。
如果怀疑 TLS/HTTP 层面的问题,需要看请求和响应是否匹配,最推荐配合tshark或者 Wireshark 做筛选分析。纯命令行上看,-A参数能打印包里的 ASCII 内容,对调试文本协议非常方便,但要避免打印二进制内容。
另外,抓包行为本身有安全风险,尤其是在生产环境。除了要控制抓包时长,抓下来的 pcap 文件往往包含敏感数据,用完后记得立刻删除或者加密保存,这个习惯一定要养成。
7. 五分钟快速定位:一个完整的排查思路
工具已经都介绍完了,最后我给出一个常规排查流程。当收到告警或者用户反馈“系统变慢”时,我上机后按这个顺序走一遍,最多五分钟就能锁定问题方向。
第一步,uptime+top看一眼整体:load 高不高,CPU 各维度占比是否异常;第二步,vmstat 1 5看连续趋势:r 队列、cs 切换、si/so、wa 的变化;第三步,free -h看 available 是否充足;第四步,iostat -x 1 3看磁盘 util 和 await 是否匹配;第五步,ss -s和ss -tin快速检查网络连接状态。这几步的判断逻辑可以对照下面这个表:
| 现象 | 最可能瓶颈 | 下一步动作 |
|---|---|---|
| us 高,r 队列长 | CPU 算力不足 | pidstat 找进程,perf 找热点 |
| wa 高,await 涨,util 饱和 | 磁盘 IO 慢 | iotop 找进程,再切 iostat 验证 |
| si/so 非零,available 低 | 内存不足 | 查泄漏或扩容,别急着动磁盘 |
| Recv-Q 积压,CPU idle | 应用处理能力不足 | 看线程池和应用日志 |
| sy 高,cs 高 | 系统调用或锁竞争 | strace -c 或 pidstat -w 定位 |
有一次最典型的排查经历:某服务高峰期延迟从 5ms 涨到 50ms,我按上述流程走完,load 正常、内存正常、磁盘 util 在 20% 以下,但 vmstat 的 cs 高达 20 万。继续用pidstat -w发现线程被动切换量异常,最终定位到服务里一个全局锁导致的高并发竞争,锁粒度太粗,全请求排队。代码改完,切换量直接回落到 2 万,延迟恢复。这类问题如果只盯着 CPU 和内存,可能抓破头皮都找不到。
7.1 常见误区清单:这些坑我踩过
排查做多了,有些错误几乎是强制重复的,我总结成录,供你对照:
第一,不要在负载最高峰的时候上strace -p去跟踪进程,大多数进程会直接因为 ptrace 的停顿变得完全不可用。
第二,free -h看到 used 高不用惊恐,先确认 available,再确认是不是真的内存吃紧。很多人看到 used 高直接 kill 了业务进程,缓存被当成泄漏,重启后性能反而回不去了。
第三,iostat的 util 只是设备忙的百分比,不是性能上限百分比。机械盘和 SSD 的评判标准完全不一样,跑一次fio基准测试拿到基线,再对比日常数据才科学。
第四,生产环境抓包要立刻落盘,不要打屏。有些时候打印出来的字符串包含敏感信息,也会因为拖慢抓包进程导致丢包,即使抓到也不是真实情况了。
第五,任何工具的输出都不是全量事实。top 里的进程排序默认按 CPU 排序,但某个进程 IO 占用很大,CPU 占比不高时,它不会在 top 最上面,这时恰恰最需要iotop。
7.2 先装工具:等出事再装就来不及了
最后分享一个非常实在的习惯:在服务器初始化的脚本里,就把诊断工具装齐。尽量保证包含 sysstat(提供 vmstat、iostat、mpstat、pidstat、sar)、dstat、iotop、iftop、tcpdump、strace、perf,能覆盖 90% 以上的排查需要。出现过太多次情况:凌晨两点告警,上机器一看iostat: command not found,还得去现装,一折腾就是半个多小时,业务已经损失惨重了。
还有一种小情况值得注意:装了 sysstat 之后,建议把/etc/sysconfig/sysstat或对应配置里的采集周期打开,让sadc定期写入系统活动数据。这样当问题发生时,你可以用sar -q -r -u -n DEV -d直接看过去某个时间点的历史性能,根本不需要“案发时刻”在场。很多时候问题都是瞬态,人不在现场,历史数据就是唯一的证据。
我个人在实际操作中的体会是:用熟一个工具,远比背十个工具的 man 手册更有价值。关键是理解每个指标背后的含义,以及它们之间怎么相互印证。你就从vmstat 1 5开始,把它的每个数字都弄明白,一旦读懂了这张表,后面再学其它工具,思路会一下子打开。