1. 几台服务器同时卡成PPT,我是怎么从一堆命令里锁定真凶的
先说个真实场景。上个月某天上午,业务群突然炸了,说后台系统点啥都转圈,登录都费劲。我登录服务器一看,好家伙,top一敲下去,CPU占用率直接顶满,load average飙到30多,这机器总共才16核。但奇怪的是,再细看进程列表,排前面的应用进程CPU也就占了百分之二三十,剩下的资源不知道被谁吃光了。
这时候光靠top已经看不出名堂了,我转头掏出iostat、sar、df挨个查了一遍,最后才定位到是一块数据盘在做大量随机读写,把整个系统的I/O队列堵死了,CPU全在等I/O,看起来像"CPU满",实际上是"I/O堵"。这种排查过程里,top、df、iostat、sar四个命令缺一不可,它们分别看的是系统资源的不同侧脸:top看CPU和内存的实时快照,df看磁盘空间和inode,iostat看磁盘I/O能力和压力,sar看历史趋势和资源累计状态。
很多刚接触Linux的朋友习惯把"卡了就先看top",这没错,但top只能告诉你系统现在"什么状态",不能告诉你"为什么到这个状态"。而df、iostat、sar正好补上了这个缺口。这篇文章我不打算照搬man手册,而是结合我实际排查服务器问题时的思路,把这四个命令掰开揉碎讲清楚——每个命令看什么、怎么看、常见坑在哪、几个命令怎么配合着用。如果你平时只是会敲top看一眼CPU,或者df -h看个磁盘剩余,那这篇文章值得花几分钟看完。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. top命令不能只看CPU:那一串数字里的系统呼吸节奏
2.1 top的头部信息藏着哪些判断依据
很多人敲top之后,目光直接锁定%CPU那一列,然后开始找谁占得高。这没错,但top头部的统计信息信息量比进程列表大得多,尤其是load average。
code复制top - 10:15:22 up 23 days, 4:12, 2 users, load average: 8.47, 7.32, 6.15
Tasks: 212 total, 2 running, 210 sleeping, 0 stopped, 0 zombie
%Cpu(s): 12.5 us, 3.2 sy, 0.0 ni, 80.1 id, 4.2 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 32108.5 total, 5123.4 free, 10245.2 used, 16739.9 buff/cache
MiB Swap: 8192.0 total, 8192.0 free, 0.0 used
load average后面的三个数,分别是过去1分钟、5分钟、15分钟的平均负载。这个数字不是CPU使用率,而是处于可运行状态和不可中断睡眠状态的进程平均数量。通俗点说,就是"排队等CPU的进程有几个"。
判断负载是否过高,不能只看数字大小,得结合CPU核心数。比如8核的机器load到8,意味着每个核平均一个任务在跑,系统基本满负荷但不一定卡;如果16核机器load只有4,那说明资源还有大量富余。真正需要警惕的是load长期高于CPU核数,这种时候进程排队等待时间变长,用户体感就是卡顿。
但load高不一定等于CPU忙。我在开头提到的那个案例,load平均30+,但CPU的us(用户态)其实不高,wa(等待I/O)占了很大比例——这种情况用top的CPU行一眼就能看出来。所以top的正确打开方式是:先看load,再看%Cpu(s)那一行的wa和us,最后才看进程列表。wa如果持续很高,问题就不在CPU而在磁盘I/O,这时候继续盯top就是浪费时间,该切到iostat了。
2.2 进程列表里的状态和内存列怎么读才有效
top默认按CPU占用率排序,但我们经常碰到的情况是:排在前面的是自己的应用进程,CPU占用也就20%,但整个系统已经很卡了。这时候别急着下结论,按大写P键按CPU排序、按大写M键按内存排序,反复切几次看看。
进程状态列(STAT)里,R表示运行中,S表示睡眠,D表示不可中断睡眠(通常是等待I/O),Z表示僵尸进程。如果发现大量进程处于D状态,那几乎可以确定I/O有问题。有一回我排查一个数据库服务器,top里一大片D状态,最后确认是底层存储的磁盘挂了导致读写卡死。如果看到Z状态僵尸进程,代表父进程没有正确回收子进程,这种通常不会把系统拖垮,但出现在Java应用里往往意味着代码有bug。
内存那一栏,RES是物理内存占用,VIRT是虚拟内存,SHR是共享内存。日常关注RES就够了,VIRT偶尔有惊喜——被CVE漏洞扫描吓过几次,就是因为VIRT显示几十G以为内存泄漏,其实那是Java进程预留的虚拟地址空间,不代表真实占用。真正要看内存压力,得看top里MiB Mem这一行的buff/cache。buff/cache是系统用空闲内存做的文件缓存,如果可用内存不足,系统会自动回收这部分,但如果cache一直占据大量内存且free很少,也不一定有问题,要看swap是否被使用。Swap used如果持续增长,那才是真的内存吃紧。
2.3 top的交互操作和几个实用变体
top进入交互界面之后,有几个键我用的频率极高:
- 数字1:展开每个CPU核心的使用率,看是不是单个核被打满而其他核空闲(这种通常是单线程应用或中断绑核问题)。
- 大写H:切换线程视图,排查Java等多线程应用时,能直接看到是哪个线程在烧CPU。
- 大写P / 大写M:按CPU或内存排序。
- 小写e / 大写E:切换内存显示单位,方便阅读。
- 小写w:把当前配置写入配置文件,下次进top还是这个视图。
实战中我经常用top -H -p <PID>看指定进程的所有线程占用的CPU,配合jstack去抓Java线程栈。还有个变体是top -d 2,把刷新间隔改成2秒,比默认的3秒稍微灵敏一点,适合排查抖动型问题。如果只是要一次性输出当前状态而不进入交互界面,可以用top -b -n 1,这在写脚本采集系统状态时很有用。
3. df -h是入门命令,但99%的人没看全df的全部信息
3.1 df到底在查什么:不只是"还剩多少G"
df是最常用的磁盘命令,绝大部分人用df -h看一眼剩余空间,没了。但实际上,df能提供的信息远不止这些。
code复制文件系统 1K-块 已用 可用 已用% 挂载点
/dev/sda1 103208344 4123456 93983288 5% /
tmpfs 617016 1024 616000 1% /dev/shm
/dev/sdb1 412708032 386002752 1844168 100% /data
第一列是文件系统设备,第二列到第五列是总量、已用、可用、使用率,最后一列是挂载点。排查磁盘问题时要关注的不仅是使用率,还有挂载点——/根分区和/data数据分区要分开看,因为某个分区满了不会直接拖垮系统盘,但会把写这个分区的服务搞挂。
比如数据库数据目录在/data,结果/data空间100%,那数据库直接无法写入。但这时系统的根分区/可能是正常的,登录、敲命令都没问题,所以很多人一开始根本不明白为什么服务挂了。磁盘告警别只看"服务器磁盘空间",要精确到具体挂载点。
3.2 -h是给人看的,-i才是救命稻草
df -h显示的是块设备的使用情况,但Linux文件系统里还有一个独立于块空间的资源:inode。inode是文件系统存储文件元数据(权限、属主、大小、时间戳、数据块指针等)的结构,每个文件或目录都要占用一个inode。如果inode耗尽,即使磁盘还有大量空间,你也无法创建任何新文件。
用df -i查看:
code复制文件系统 Inode 已用 可用 已用% 挂载点
/dev/sda1 6553600 6522133 31467 99% /
我遇到过两次inode耗尽的问题。一次是某个程序疯狂创建小文件,每分钟几万个,把根分区的inode占光了,结果连系统日志都写不进去,现象是各种服务陆续crash。另一次是邮件系统队列里积压了海量小文件,df -h一看空间还有50%,但服务就是起不来,查了半天才意识到是inode爆了。
所以大促封网前我都会顺手跑一遍df -h和df -i,两个都看才放心。要找哪些目录的文件数量异常,可以用find / -type f | wc -l粗略数一下,或者用for i in /*; do echo $i; find $i -type f | wc -l; done按顶层目录统计文件数,快速定位是哪个目录在造inode。
3.3 删了文件空间却不释放,这个经典坑的根因和排查
还有一种高频故障:明明把一个大文件删了,df一看,可用空间还是没涨。这个问题的本质是:进程仍然持有被删除文件的文件句柄,Linux在进程关闭句柄之前不会真正释放这块磁盘空间。
排查办法用lsof:
code复制lsof | grep deleted
输出里能看到类似进程名 PID 用户 文件描述符 文件类型 设备 大小 删除状态 路径的记录。记下PID,然后重启对应的服务或进程,空间就会释放。如果你不想重启服务,可以试试用echo > /proc/<PID>/fd/<文件描述符>把文件内容清空,但这个方法风险较高,不太建议在业务高峰期操作。
还有一个容易忽略的点:df显示的"已用"和du统计出来的"实际文件占用"经常对不上。这不是bug,而是因为du按目录统计时不会计入被删除但句柄还开着的文件,也不会统计已分配的保留块。如果df明显大于du的统计结果,要么是句柄未释放,要么是文件系统里有隐藏的保留区或快照(比如LVM、btrfs、zfs的快照),这时候就要看卷管理器了。
4. iostat:I/O到底饱和没有,别被%util骗了
4.1 第一次把iostat当"磁盘工具"用,我犯过的错
我刚工作时,把iostat归类为"磁盘性能工具",后来发现这个理解太片面了。iostat真正的价值在于,把CPU和磁盘I/O放在同一张报表里对比着看,这正好解决了前面top里wa高却不知道哪块盘在拖后腿的问题。
code复制avg-cpu: %user %nice %system %iowait %steal %idle
8.42 0.00 2.36 18.65 0.00 70.57
Device tps kB_read/s kB_wrtn/s kB_dscd/s kB_read kB_wrtn
sda 2.35 12.45 84.12 0.00 4582192 30987121
sdb 45.82 228.94 466.20 0.00 46928455 96256881
avg-cpu部分能看到%iowait,这个值如果持续大于10%,基本可以判断系统在等I/O。然后往下看Device列表,哪个设备的tps和读写速率明显偏高,哪个就是瓶颈盘。上面这个例子里sdb的tps是45.82,读写总量远大于sda,基本可以确认压力集中在sdb上。
这也是为什么我推荐排查性能问题的时候,把iostat作为第三板斧——top看CPU,df看空间,iostat看I/O压力。三个命令一配合,系统的资源状况基本就勾勒出来了。
4.2 -x参数下的核心指标,哪些真正决定你要不要处理
用iostat -x 1可以输出扩展统计,这里面有几个指标需要真正理解,而不是看着数字焦虑。
code复制Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
sdb 12.45 33.37 228.94 466.20 0.00 8.17 0.00 19.67 0.42 0.83 0.03 18.39 13.97 0.20 0.92
- %util:设备忙闲比例,接近100%说明设备基本被占满。但这里有个经典误区:%util高并不一定等于性能瓶颈,尤其是现在的SSD,很多盘的队列深度很深,%util到100%依然能扛住极高的吞吐。反过来,%util不高但延迟很高,也可能是I/O路径上有其他问题(比如磁盘坏道重试、RAID降级、网络存储抖动)。
- await:I/O请求从发出到完成平均耗时,单位毫秒。传统机械盘await超过20ms就该警惕,SSD通常应该在个位数毫秒。这个指标直接反映用户侧体验,比%util更能说明问题。
- svctm:设备处理一个I/O请求的平均时间。注意,这个指标在很多新版iostat里已经不太可靠了,尤其是SSD和虚拟化环境下,它的参考意义明显下降。真正要看服务时间质量,建议结合await和%util一起判断。
- aqu-sz:平均队列长度,这个数值越大,说明有越多请求在排队,系统I/O压力越大。
判断逻辑供参考:如果await高、%util高、队列长,说明磁盘确实忙不过来,要么升级硬件、要么减少读写的频率。如果await高但%util不高,说明单个请求处理慢,可能是磁盘坏道、RAID重建、或存储网络问题。如果%util高但await正常、吞吐也大,那很可能这只盘本来就该干活,把业务错峰或控制并发就行。
4.3 用iostat做压力测试前的基线采集
我上线新存储或调整数据库参数之前,习惯先跑一轮I/O基线:
bash复制iostat -x 1 10
每秒钟采一次,连续采10次。取平均值记下来,包含sdb的r/s、w/s、await、%util等。这组数据就是这台机器当前业务模式下的"常规水位"。下次再出问题,先拿新数据和基线对比,如果同样业务量下await翻了三倍,那不用看别的,存储链路肯定有变化。
如果觉得iostat的实时输出不够直观,可以配合iostat -x 1的重定向输出到文件,等故障发生后再回头看是哪个时间点开始异常。不过说实话,如果真想看历史和趋势,sar比iostat更适合。
5. sar:服务器宕机一天后才想起的后悔药
5.1 为什么我每次都劝别人装sysstat并开启cron收集
sar是sysstat包里的核心工具,它的独特价值在于可以回溯历史数据。平时系统正常的时候,你可能觉得sar完全没有存在感;一旦某天凌晨两点数据库慢查询、早上起来发现磁盘报警,如果没有sar的历史数据,你只能靠回忆和猜测。而sar只要你提前开了收集,所有数据都在那里躺着,随时可以回放。
安装sysstat:
- Debian/Ubuntu:
apt install sysstat - CentOS/RHEL:
yum install sysstat
装完之后,需要确保系统统计任务在运行。Debian系需要编辑/etc/default/sysstat,把ENABLED="false"改成"true"(CentOS系默认开着)。然后重启sysstat服务:
bash复制systemctl restart sysstat
sysstat默认通过cron每10分钟采集一次数据,数据存在/var/log/sa/目录下,文件名是sa加日期,比如sa20250517。保留天数可以配置/etc/sysstat/sysstat里的HISTORY参数,我习惯设成30天,这样能覆盖大多数故障回溯场景。
5.2 不是所有性能问题都能等到你抓现场,sar能回放历史
实时状态下你还能用top、iostat抓现场,但半夜3点的故障,等你起床早就过去了。sar弥补的就是这个时间差。
我常用这几个参数:
sar -u:CPU使用率历史,能看出系统是持续高负载,还是某个时间段突然飙高。sar -r:内存使用情况,重点看kbmemfree、kbmemused、kbcommit,判断是否存在内存增长趋势。sar -q:系统负载历史,包括runq-sz(运行队列长度)和load average,这是判断系统"什么时候开始扛不住"的最直接依据。sar -b/sar -d:I/O传输速率和设备活动,配合%util看哪个时段磁盘压力最大。sar -n DEV:网络流量,公司带宽被打满时非常好用。
比如有一次业务方反馈每天早上9点半到10点之间系统特别慢。我直接敲:
bash复制sar -q -f /var/log/sa/sa20250517
结果发现9:30开始load从2跳到15,9:45达到高峰。再看sar -n DEV,发现同一时间网卡入口流量暴增。最终定位到一个批量数据同步任务每天9点半准时启动,把带宽打满了。整个过程没装任何额外监控,靠sar的存量数据十几分钟就定位了。
5.3 从sar报表里读出的运维习惯:别等问题发生才想起来
sar还有个容易被忽略的用法,是做容量规划或日常巡检。我每周会跑一次sar -u -f把一周的数据拉出来,看CPU使用率有没有缓慢上升的趋势;sar -r看内存commit率的走势。这些趋势数据就像体检报告,在故障爆发之前就能发现异常苗头。
另外提醒一下,如果你把/var/log/sa/所在的目录放在一个空间很小的分区里,时间长了会产生不少数据。默认10分钟一次的频率,一天的数据量也就几MB,但保留天数设太长、机器又多的话,还是要给这个目录留足空间。我在生产环境一般是单独给/var/log分一个盘或者限制sa目录的磁盘占用上限。
6. 一套组合拳:CPU飚高、磁盘打满、I/O卡顿分别怎么定位
6.1 巡检和告警里的"黄金五分钟"排查流程
网上那些"Linux性能排查命令大全",把一堆命令列出来让你背,但真到了出故障的时候,人一慌根本不知道该先敲哪个。我的经验是,把排查流程固化成一个标准顺序,形成肌肉记忆。
接到告警后的第一个动作,我永远是uptime看一眼load和负载趋势,然后按下面这个顺序展开:
bash复制top # 看load、CPU us/wa、内存、进程排行
df -h # 看各个挂载点的空间使用率
df -i # 看inode是否耗尽
iostat -x 1 # 看磁盘I/O压力和延迟
sar -q -f /var/log/sa/sa$(date +%d -d yesterday) # 看昨天的负载基线
这套流程大概一分钟能跑完,覆盖了CPU、内存、磁盘空间、磁盘I/O、历史趋势五个维度。绝大多数生产故障,在跑完这套命令之后都能定位到大致方向,剩下的就是进入具体维度深挖。
6.2 场景一:CPU使用率飚到99%,先从top入手还是从iostat入手?
CPU飚高有两种常见情况,处理路径完全不同。
如果top里%Cpu(s)的us那项很高,比如80%以上,而wa很低,这是真·CPU密集。这时候按P排序看进程,锁定PID后如果有必要,再按H切线程视图,确认是哪个线程在烧。接下来根据进程类型决定下一步:Java应用就jstack抓线程栈,Python应用就py-spy dump,其他类型就strace -p看系统调用。
如果us不高,wa却很高,那就是CPU在等I/O。这时候别在top里浪费太多时间,立刻切到iostat -x 1看哪块盘的await暴涨。我见过太多人扛着top反复刷新,等了十分钟还是同样的画面,最后才想起来看I/O。
还有一种容易被忽略的情况:%Cpu(s)里的st字段,表示虚拟机被hypervisor抢占的时间。在云服务器上如果st持续很高,大概率是宿主机资源争抢,你在虚拟机里怎么优化都作用有限,这时候该找云服务商反馈了。
6.3 场景二:df和iostat数据打架,先信哪个?
有时候df显示/data使用率已经95%,但iostat看I/O压力并不大。这种情况通常说明磁盘慢不是空间问题,而是文件数量太多导致元数据操作慢(比如目录里有几百万个小文件,ls都卡半天),或者文件系统碎片严重。
反过来,iostat显示r/s和await非常高,df显示空间还有70%,这种情况常见于日志文件频繁追加和删除、数据库频繁读写等。判断逻辑很简单:df看的是"容量水位",iostat看的是"活跃度"。容量高不一定活跃,活跃高不一定容量满。真正要命的是两个同时高——容量快满、I/O又繁忙,意味着磁盘的写入路径在接近极限状况下工作,故障概率会大很多。
有一次我们遇到过df空间还剩20%,iostat的w_await飙到300多ms的情况。开始以为是磁盘坏了,后来发现是一个日志切割脚本每隔几秒就清理一次日志文件,导致文件系统锁竞争激烈,写操作全堵在锁上。把日志清理策略改成每天一次之后,await立刻降了下来。这类问题靠单个命令很难看出来,必须df+iostat放在一起对比才能发现"空间还够但I/O很忙"的不协调状态。
6.4 场景三:top一切正常但业务很慢,问题藏在网络
最后补充一个反直觉的场景:top、df、iostat全都是健康的,但业务就是慢。这种时候如果你还在系统层面钻牛角尖,可能查一天都没结果。正确的做法是打开sar -n DEV看看网络流量是否有异常,再配合ss -s看socket连接状态、ss -tn看TCP连接数。
有一次线上系统偶发卡顿,所有系统指标都正常,最后定位到是某个外部接口调用超时,TCP连接被占满,新请求全在排队。系统本身没有任何资源瓶颈,纯粹是外部依赖拖慢了业务。这种问题用top、df、iostat是查不出来的,得靠网络类工具和链路追踪——所以排查问题千万别被工具局限住,工具只是帮你排除变量的手段。
7. 高频面试题背后的实战含义
7.1 load average高但CPU使用率低,怎么解释?
这是面试里很容易考到的一道题,也是现实中非常常见的一种误判。
load average包含了处于R状态和D状态的进程数。CPU使用率低但load高,说明大量进程在D状态——通常就是等I/O。所以答案不是"系统很闲",而是"有进程被I/O堵住了"。反过来如果load高、CPU也高,那就是真的计算密集。记住这个区分,很多排查就不会走弯路。
7.2 iostat的%util接近100%就一定需要扩容吗?
不一定。%util接近100%只能说明设备在采样周期内一直有请求在处理,但如果请求都是小块随机读且吞吐不大,扩容未必能解决问题。更好的判断维度是await和队列长度:如果await已经超过业务能接受的延迟阈值,或者aqu-sz持续大于设备最优队列深度,这时候才真的需要扩容、拆分I/O或做其他优化。
7.3 磁盘空间还剩50%但应用报"磁盘满",可能是什么问题?
这个问题的标准答案是inode耗尽。但实际工作中还有两个常见原因:一是写入了超出挂载点空间的临时文件(比如/tmp所在分区是tmpfs,重启就清空,平时看着不大,突然被写满直接导致系统临时文件创建失败);二是分区挂载权限变了或目录被只读挂载(比如文件系统错误导致系统以只读方式重新挂载)。所以我遇到"应用报磁盘满"时,除了df -h和df -i,还会顺手mount看一下挂载选项里有没有ro。
8. 最后聊聊我的实际使用习惯
工具贵在趁手,不在多。top、df、iostat、sar这四个命令我用了十年,组合起来能覆盖日常运维80%以上的系统排查场景。我自己的习惯是:每次登录服务器先uptime看一眼load趋势,每周跑一次df -h和sar -u做例行巡检,遇到性能问题就按第六节那套流程走一遍。如果服务器数量多,还可以把这些命令写进脚本,统一采集汇总到监控平台,这样比一台台手动敲高效得多。
最后送大家一条压箱底的建议:多看看/var/log/sa目录下的历史数据,养成这个习惯之后,你对服务器性能的理解会从"抓现场"升级到"看趋势",很多疑难杂症在你眼里会变得有迹可循。毕竟,最好的排查工具不是等你出问题时才打开的那个,而是一直在默默记录的那个。
