做运维和开发的朋友十有八九都遇到过这种场景:线上服务器突然飘红,CPU跑满、内存告急、磁盘IO卡到业务超时,收到告警后第一时间登上机器,却不知道该从哪条命令入手。这时候手里有没有一套顺手且好记的Linux系统性能排查常用指令,基本就决定了你是10分钟止血,还是被问题拖住一整个下午。
这篇文章我用自己多年排查线上故障的实际经验,把Linux性能排查的路线、指令和读数方法串起来讲清楚。适合刚入门运维想建立排查体系的同学,也适合经常在服务器上处理问题的后端研发和SRE参考。内容涵盖CPU、内存、磁盘IO、系统负载这几大块,每个指标怎么读、什么时候报警、怎么顺着命令一路揪出元凶,都会写到,提供的指令和判断基准都是可以直接抄作业的那种。
1. 排查思路与指令体系的整体设计
上手敲命令之前,得先想明白一件事情:性能排查不是把top、free、vmstat全敲一遍,而是“带着问题去找数据”。如果在CPU告警时去盯着free看半天,或者在内存不足时反复看top的进程列表,方向就歪了。
1.1 从“症状”到“根因”的三层思路
我把整个排查过程分成三层,每一层对应不同的指令目标。
第一层是看整体状态。登进机器之后不要急着看进程,先用uptime和top这类命令看系统整体负载、CPU空闲比例、内存剩余情况,回答一个核心问题:这个节点现在到底是什么资源最紧张?这一步的目标是把“哪里有问题”框定出来。
第二层是找具体进程。整体状态确认之后,再用top或者ps等指令锁定是哪个进程在消耗资源。举个例子,如果第一层发现CPU几乎跑满,那第二层就要确认究竟是Java服务还是数据库进程在吃CPU,把范围从服务器收敛到进程。
第三层是分析根因。进程锁定之后,用vmstat看上下文切换和运行队列,用iostat看IO等待,用pidstat配合线程号定位到具体线程,甚至用perf采样看热点函数。这一层的目标是回答“为什么是它在消耗”。
这样三层走下来,整个排查过程就是一条清晰的链路:节点到进程、进程到线程、线程到代码。反向操作很容易出问题——先看了一堆细粒度指标,最后连整体状态都没搞清,等于带着放大镜跑马拉松。
1.2 常用性能排查指令全景图与选型逻辑
Linux性能排查的命令工具非常多,但真正高频使用、能覆盖绝大多数场景的其实有限。我按排查目标做了个整理:
| 排查目标 | 首选指令 | 辅助指令 | 典型场景 |
|---|---|---|---|
| 整体负载 | uptime | top、w | 判断当前机器是不是已经过载 |
| CPU使用率 | top | mpstat、pidstat、perf | 定位CPU占用高的进程和线程 |
| 内存使用 | free | top、smem、pmap | 判断物理内存是否不足或泄漏 |
| 磁盘空间 | df | du、lsof | 排查磁盘满导致的写入失败 |
| 磁盘IO | iostat | iotop、pidstat -d | 定位IO等待和磁盘瓶颈 |
| 网络连接 | ss | netstat、sar -n DEV | 排查连接数过高、流量异常 |
| 历史性能数据 | sar | - | 复盘故障时间点的现场 |
选型逻辑很简单:优先用系统自带的、输出信息密度高的命令,实在不够再上性能分析工具。为什么我很少推荐一上来就装perf、systemtap这种重型分析工具?因为线上环境安装权限不一定有,而且对新手来说输出太难解读。top、vmstat、iostat、free这一套是每个Linux发行版默认自带的,覆盖面也足够应对九成以上的问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心指令输出拆解:数据该读哪一列
命令敲下去,输出一大屏,真正需要关心的往往只有两三列。这一节我把每个高频指令的关键读数列出来,并给出我平时用的判断阈值和解读思路。
2.1 uptime与整体负载:load average怎么看
uptime是性能排查的第一个命令,它输出一行很关键的数据:load average。这个值包含1分钟、5分钟、15分钟三个数,分别代表过去1分钟、5分钟、15分钟的平均活跃进程数。
很多人看到load超过1就以为机器挂了,其实这是误解。load average要结合CPU核数来判断:如果你的机器是8核,load在8左右说明刚好满载;超过8说明有任务在排队;如果是2核机器,load到4就已经相当危险了。判断标准可以简单记成:load值除以CPU核数,超过0.7就要警惕,持续超过1.0就需要介入处理。
还要注意一个细节:load average高不代表CPU一定忙。它统计的“活跃进程”包含正在运行的进程和不可中断睡眠状态的进程。后者通常出现在等待磁盘IO时,所以当你看到load很高但CPU空闲率也很高时,大概率是磁盘IO把进程堵住了,这时候继续折腾CPU相关命令就是在浪费时间,应该转向iostat。
2.2 top:进程列表和CPU状态段的正确解读
top是排查CPU问题的核心工具。它的输出分成两部分:上面是系统概况区,下面是进程列表区。
系统概况区里最关键的是%Cpu(s)那一行。其中us代表用户态CPU占用,sy代表内核态CPU占用,wa代表等待IO完成的CPU占比,id是空闲占比。如果wa长期偏高,说明CPU在等磁盘,瓶颈不在CPU本身;如果us或者sy过高,才是真正的CPU计算瓶颈。
进程列表区需要关注的是%CPU、%MEM、RES和S列。%CPU和%MEM是进程的资源占用比例,RES是物理内存占用,S是进程状态。S列如果是R表示运行中,S表示睡眠,D表示不可中断睡眠,D状态大量出现时基本可以确认磁盘IO有问题。
使用top的时候我有两个习惯。第一个是启动后按数字键1展开每个CPU核心的使用情况,很多单核被打满的问题,在总览里只能看到60%左右的使用率,但展开后会看到某一个核心已经100%了。第二个是进入top后用P键按CPU排序、用M键按内存排序,不要靠肉眼在默认列表里找异常进程。
2.3 vmstat:系统级瓶颈探测利器
我平时判断系统瓶颈类型时喜欢用vmstat,因为它的字段设计得很精妙,一条命令就能把CPU、内存、IO的线索全带出来。常见的用法是vmstat 1 5,每隔1秒采集一次,连续采集5次。
输出里需要重点看这几个字段:
- r:运行队列中的进程数。这个值持续大于CPU核数,说明CPU已经忙不过来了。
- b:不可中断睡眠进程数。这个值偏高,大概率是磁盘IO阻塞。
- si和so:swap换入换出的数据量。只要这两个值长期不为0,说明物理内存已经不够用,系统正在用磁盘充当内存,性能会被拖垮。
- wa:CPU等待IO的时间占比。和top里的wa对应,持续高说明IO子系统是瓶颈。
- cs:上下文切换次数。数值特别高的时候,要考虑是不是线程开太多或者锁竞争严重。
举个例子,我遇到过一台机器load很高但us和sy都不算高,当时就是靠vmstat发现b列有十几个进程,再配合iostat确认是磁盘在读盘上卡住了。如果当时只盯着top看CPU,可能半天都定位不到问题。
2.4 free:内存别只看已用和剩余
free是我见过被误读最多的命令。很多人用free -h看到used很高、free很低,就判断“内存不足”,这其实是个误区。
Linux的内存管理机制决定了它会尽量把空闲内存用作buff和cache来提升读写性能,这部分内存在进程需要时是可以释放出来的。所以看内存余量,不要只看free列,要看available列,也就是“在不触发swap的情况下,还能分配给新进程的内存估算值”。只要available足够,哪怕free显示为0,也不需要紧张。
判断内存是否真的吃紧,我习惯结合两个信号:一是available已经很低,二是vmstat里si和so开始有持续的换入换出动作。这两个信号同时出现,才需要认真评估是加内存还是排查进程泄漏。单纯看数字低就重启服务,属于“治标不治本”的解决方式。
2.5 iostat与mpstat:盘和核的细节指标
排查磁盘IO时,iostat -x 1是我首选的指令。-x参数会展示更详细的扩展数据,每秒采集一次。
%util这个参数含义是设备在采样周期内处理IO请求的繁忙程度,很多人把它等同于磁盘利用率,其实不准确。它反映的是磁盘是否一直处于工作状态,一个忙碌的磁盘可能存在排队。真正重要的是await,它表示IO请求从发出到完成平均消耗的时间。机械盘在10毫秒以下算正常,持续几十毫秒说明磁盘已经吃力了;SSD的话,几毫秒的await是基本要求。还有一个容易忽略的指标是w_await和r_await分开看,很多业务是读多写少,如果读延迟正常写延迟高,那问题方向就完全不同了。
mpstat和iostat是配套使用的。mpstat -P ALL 1能看到每个CPU核心各自的占用率。尤其在排查多线程程序时,如果总的CPU使用率只有30%,但某一个核心已经跑到100%,那多半是代码里存在单点竞争,某个线程把单个核心打满了。这种问题在只看top总览的情况下很容易漏掉。
2.6 sar:把性能数据存下来复盘
性能排查最怕的是:机器现在没有问题了,但你不知道刚才到底发生了什么。sar就是用来解决这个问题的。
sar是sysstat包里的核心工具,前提是系统已经安装并启动了数据采集服务。正常情况下,系统会定时采集CPU、内存、磁盘、网络等数据并落盘,事后可以通过sar -u看CPU历史使用率,sar -r看内存历史占用,sar -d看磁盘历史压力,sar -n DEV看网络吞吐。
我自己的习惯是,在改完配置、上线新版本之后,隔一段时间就回到sar里拉一下数据做对比。判断一台机器的性能变化,不能只看当前这一秒的状态,而要通过15分钟、1小时、1天的趋势曲线来判断是突发尖峰还是缓慢爬坡。没有历史数据做基线,值班排查问题时就会非常被动。
3. 完整实操复盘:三类典型故障的排查过程
理论说了一堆,不如完整跑一遍流程直观。这一节我模拟三个线上真实高频故障的排查过程,把每一层用了什么命令、读到了什么数值、基于数值做了什么判断都写出来。
3.1 CPU飙高定位线程与代码
故障描述:线上告警某Java服务所在服务器CPU使用率超过80%,接口响应时间明显变长。
我登进机器的第一件事是执行uptime确认当前整体负载:
bash复制$ uptime
14:22:01 up 120 days, 2:14, 1 user, load average: 6.08, 3.55, 2.10
机器是4核,load average的1分钟值已经到6,远超核数,说明CPU确实存在排队。接着用top确认进程:
bash复制$ top -b -n 1 | head -20
top - 14:22:10 up 120 days, 2:14, 1 user, load average: 6.08, 3.55, 2.10
Tasks: 208 total, 3 running, 205 sleeping
%Cpu(s): 82.3 us, 15.2 sy, 0.0 ni, 0.0 id, 0.0 wa
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3841 appuser 20 0 32.6g 2.1g 32m S 176.0 26.7 123:20.13 java
到这里,方向已经很清晰了:java进程的CPU占用率达到176%,超过了单核能力,并且CPU整体us占比82.3%。接下来的问题是如何定位到Java内部。先用pidstat把该进程内的线程CPU占用抓出来:
bash复制$ pidstat -t -p 3841 1 3
14:22:40 UID TGID TID %usr %system %CPU CPU Command
14:22:40 1001 3841 4001 65.0 3.2 68.2 2 java
14:22:40 1001 3841 4008 20.1 1.0 21.1 0 java
看到线程4001占用68%之后,需要通过jstack把线程栈拉出来,确认它到底在执行什么代码。这里有个小技巧:jstack默认输出的线程号是十六进制,需要把4001转成十六进制再搜索。
bash复制$ printf '%x\n' 4001
fa1
$ jstack 3841 | grep -A 30 'fa1'
"http-nio-8080-exec-8" #48 daemon prio=5 os_prio=0 tid=...
java.lang.Thread.State: RUNNABLE
at com.example.service.OrderService.getUserOrderList(OrderService.java:120)
...
到了这一步,问题就精确定位到了OrderService这个类的第120行。整个流程从登机到定位代码行,耗时不到5分钟。这个案例想说明的关键点是:top和pidstat负责把范围缩小,jstack负责给出最终答案,反过来操作是没有用的。
3.2 磁盘IO瓶颈找出元凶进程
故障描述:应用报错文件写入超时,机器整体load偏高,但CPU空闲率却很高。
先用uptime看到load居高,然后直接top看CPU状态:
bash复制$ top -n 1
%Cpu(s): 3.5 us, 2.1 sy, 0.0 ni, 85.0 id, 9.4 wa
这个数据非常典型:wa占了9.4%,这是CPU空转在等待IO操作的典型特征。再配合vmstat确认:
bash复制$ vmstat 1 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 12 0 80204 102040 820122 0 0 8210 1990 1241 1510 3 2 85 9 0
b列有12个进程处于不可中断睡眠状态,bi每秒读入8210KB,说明磁盘确实在承受大量读取。接着用iostat定位磁盘负载:
bash复制$ iostat -x 1 2
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s await r_await w_await %util
sda 12.00 16.00 320.5 105.0 12048.0 3044.0 18.50 20.10 14.80 98.6
%util已经到98.6%,awit 18.5毫秒对这个磁盘规格来说确实偏高了。整体来看就是磁盘IO队列过长。下一步要找出是哪个进程在疯狂读盘,用iotop直接看:
bash复制$ iotop -o -P -b -n 3
PID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
18231 be/4 appuser 12.88 M/s 0.00 B/s 0.00 % 98.20 % java
到这里就能确认是某个Java进程在大量读取磁盘,接下来去应用日志里看它在这个时间点执行了什么任务即可。整个排查的核心逻辑就是:wa高不代表CPU坏,先顺着vmstat的b列线索切到iostat找设备,再用iotop找进程。
3.3 内存问题评估与进程级定位
故障描述:服务器响应变慢,业务反馈系统经常触发OOM,进程被系统杀掉重启。
先看free确认内存整体水位:
bash复制$ free -h
total used free shared buff/cache available
Mem: 15Gi 7.9Gi 1.2Gi 4.0Mi 6.3Gi 6.8Gi
总共15G,available还剩6.8G。重点来了:这看起来还有不少余量,但业务反复OOM,说明问题不是简单的“内存不够”,而像是某个进程在短时间快速申请内存。我先用top的M键按内存占用排序:
bash复制$ top -b -n 1 -o %MEM | head -20
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3841 appuser 20 0 32.6g 4.2g 32m S 12.0 27.5 123:20.13 java
12602 appuser 20 0 20.1g 3.3g 21m S 90.5 21.4 98:20.13 java
紧接着看一下进程的内存增长趋势。使用pidstat -r按秒统计,如果RES每次采样都只增不减,基本就能坐实内存持续增长:
bash复制$ pidstat -r -p 12602 2 3
14:30:01 PID minflt/s majflt/s VSZ RSS %MEM Command
14:30:01 12602 1233.5 0.0 20.1g 3.3g 21.4 java
14:30:03 12602 1241.0 0.0 20.1g 3.4g 22.0 java
14:30:05 12602 1247.5 0.0 20.1g 3.5g 22.7 java
RSS从3.3G一路涨到3.5G,同时minflt每秒一千多次,说明进程在频繁分配内存。这种情况下把堆转储出来分析对象分布基本就能定位到泄漏源。如果已经OOM了,别忘了看dmesg,里面有内核杀进程的记录,能告诉你谁占了最多内存:
bash复制$ dmesg | tail -30
Out of memory: Killed process 12602 (java) total-vm:21088040kB, anon-rss:4203000kB, ...
排查内存问题时最容易犯的错误是看到free低就直接加内存或者重启服务。有些“内存不足”其实是缓存占用高,有些是某个进程泄漏导致整体水位攀升。先分清类型再动手,才不会被表象带着走。
4. 常见误判与实用避坑技巧
很多命令不是不会敲,而是输出读错了、判断标准用错了。这一节我列一个高频误判场景表,再加上一些我踩过坑之后总结出来的习惯,算是整套内容里最有“性价比”的部分。
4.1 高频误判场景速查表
| 现象 | 常用命令 | 容易出现的误判 | 正确的判断思路 |
|---|---|---|---|
| load average高 | uptime | 以为CPU不够用 | 先看CPU空闲率,空闲率高则检查磁盘IO等待 |
| free命令free列为0 | free -h | 以为内存即将耗尽 | 看available列,cache多不代表内存紧张 |
| 磁盘%util=100% | iostat -x | 以为磁盘已经到极限 | 结合await判断,util高但延迟低可能只是操作密集 |
| 某进程CPU很高 | top | 直接kill进程 | 先用pidstat看线程,再用jstack/perf定位到具体执行点 |
| 单核CPU 100% | top总览 | 以为CPU总占用不高没关系 | 按数字1展开核心,确认是不是单核瓶颈 |
| 上下文切换频繁 | vmstat | 以为是CPU瓶颈 | 结合进程数判断,线程过多也会导致cs高 |
发现没有,大部分误判都源于只看单一指标就下结论。性能排查天然就是一个多指标交叉验证的过程,一个数字只能说明现象,至少要两个以上的指标互相印证,才能接近根因。
4.2 我踩过几次坑之后总结的排查习惯
先说一个最常见的习惯问题:很多人排查的时候只看一次实时输出,这非常容易误判。top、vmstat这类命令输出的是瞬时快照,而性能问题往往有波动性。我现在的习惯是每条命令至少连续采样3到5次,比如vmstat 1 5,通过多个采样点判断趋势,而不是看到一个高数字就动手。
再说一下关于历史数据的坑。默认安装很多最小化系统时,sysstat的采集服务可能是没启动的,等你需要看故障时间点的历史数据时,会发现里面什么都没有。所以新机器到手,我第一件事就是把sysstat装上并确认采集正常,这比装任何监控系统都基础。
还有个容易忽略的地方:排查性能问题时一定要记录时间线。进程CPU飙高是几点几分开始的,和哪个定时任务、哪次发布操作对得上,通常就是排查的突破口。命令行里执行history看一下最近的操作也好,还是对照监控系统的时间轴也好,时间线对齐往往能省下大量排查时间。
另外,kill -9一定要谨慎。很多时候进程吃满CPU可能只是临时性任务,直接杀掉会造成更大的业务影响。先看清楚进程的业务属性,再决定是调优、重启还是扩容。我自己碰过一次线上事故,就是同事看到数据库进程CPU高直接kill,结果数据库主从都崩了,教训相当深刻。
写在最后
我个人在实际操作中的一个体会是:Linux性能排查不是比谁记住的命令多,而是比谁能把有限的命令用出条理。我也见过有人把几十条命令背得滚瓜烂熟,一上来就各种冷门工具轮番上阵,结果输出越看越迷糊。反而是那些能把uptime、top、vmstat、iostat、free、sar这几条基础命令读透的同事,处理线上问题的效率往往更高。
最后分享一个小技巧:可以把这套排查流程整理成一个shell脚本,比如输入perf-check.sh cpu就自动跑top、vmstat、pidstat,输入perf-check.sh disk就自动跑iostat、iotop,把输出带时间戳追加到日志文件里。这样的好处是真正出问题时手忙脚乱,敲固定指令容易漏,脚本化之后一次就能把需要的信息全部留下来,后续分析也有数据可依。排查工具从来不是为了炫技,而是为了在最短的时间里找到答案。
