凌晨两点收到告警短信一台应用服务器 load average 飙到 30 多可 CPU 使用率才 15%。当时第一反应是这不对劲负载这么高CPU 怎么会闲着打开 top 一看十几个进程卡在 D 状态不可中断睡眠再往下查原来是 NFS 挂载的存储节点失联了。这个场景我相信不少做运维或者后端的朋友都遇到过——负载和 CPU 使用率明明是两回事可第一次排查时很容易被这两个指标绕晕。这篇就专门聊聊 Linux 系统负载查看和性能监控命令从负载三个数字怎么读到 top、vmstat、iostat 这些命令怎么配合使用再到服务器变慢时按什么顺序排查一次性讲清楚。1. load average 三个数字的真正含义先搞清楚负载到底是什么1.1 从 /proc/loadavg 说起很多人第一次接触系统负载是执行uptime命令时看到load average: 0.52, 0.58, 0.59这样一行输出。这三个数字在大多数 Linux 发行版里分别代表过去 1 分钟、5 分钟、15 分钟的平均活跃进程数。但活跃进程数这个说法太抽象了内核到底在统计什么直接看内核的数据来源最靠谱。执行cat /proc/loadavg输出一般是这种格式$ cat /proc/loadavg 0.52 0.58 0.59 2/415 23456前三个字段就是 1 分钟、5 分钟、15 分钟的负载平均值第四个字段2/415的分子表示当前正在运行的进程注意是正在运行或等待运行的进程数分母是系统总进程数第五个字段是最近一次创建的进程 PID。这个文件在内核源码kernel/sched/loadavg.c里计算用的是指数衰减移动平均算法所以 5 分钟和 15 分钟的值天然会比 1 分钟的值平滑不会因为瞬间抖动就剧烈变化。1.2 R 状态和 D 状态负载统计的是正在忙碌和排队等待的进程内核在统计负载时只关心两类进程TASK_RUNNINGR 状态正在运行或处于 CPU 运行队列中等待调度和TASK_UNINTERRUPTIBLED 状态不可中断睡眠通常是阻塞在磁盘 IO、NFS 网络文件系统等操作上。这里有个容易忽略的关键点D 状态进程虽然不占用 CPU但也被算进负载里。这就是为什么我在开头那个场景里CPU 使用率只有 15%负载却能飙到 30 多——大量进程卡在不可中断的 IO 等待上。这类进程还有一个特性连kill -9都杀不掉因为内核压根不处理它的信号只能等 IO 超时恢复或者重启机器。判断一个进程是什么状态可以直接看ps输出的 STAT 列$ ps -eo pid,state,comm | grep -E [DR] 12345 D nfsd 12346 R bash一个 D 状态进程通常在等待磁盘、网络文件系统或者硬件设备的响应。如果系统里 D 状态进程持续增多别先怀疑 CPU优先怀疑存储和网络链路出了问题。1.3 三把尺子单核、多核、容器场景下的判读标准负载值本身没有绝对的健康线必须结合 CPU 核心数来看。可以用nproc查看当前机器的逻辑核心数单核机器负载 1.0 表示刚好跑满0.7 以上就要开始关注四核机器负载 1.0 表示只用了四分之一的能力4.0 才是满载建议的经验阈值负载/核心数 0.7 健康0.7~1.0 需要观察 1.0 说明有任务在排队 核心数的 2 倍基本属于已经在拖垮业务的状态。还有一个容器场景的坑必须单独提醒在 Docker 容器或者 Kubernetes Pod 里执行uptime看到的 /proc/loadavg 其实是宿主机的全局负载不是容器自身的负载因为 /proc/loadavg 是内核全局文件没有做 cgroup 层面的隔离。如果在容器里发现负载莫名其妙很高先到宿主机上再确认一次别被误导。看容器真实 CPU 占用用docker stats或者读 cgroup 的 cpuacct 文件更准确。1.4 CPU 使用率不等于负载这个误解特别普遍以为负载高就是 CPU 忙。其实 CPU 使用率是单位时间内 CPU 忙碌的时间占比负载是有多少进程在等待被处理这两个概念完全不同。拿高速路打个比方CPU 使用率像是车道的利用率负载像是收费站前面排队的车辆数。车道利用率高CPU 100%说明路一直在跑车但如果没有排队负载可能只有 1.0车道利用率不高CPU 20%但收费站前面排了 100 辆车大量 D 状态进程在等 IO负载照样能冲到 30。所以排查性能问题时第一步不是看 CPU而是看负载背后到底是 R 状态在排队还是 D 状态在等待。2. 实时监控命令组合top、vmstat、iostat、free 的分工与读法2.1 uptime一秒钟看清全局先说最轻量的命令。uptime其实就是读取 /proc/loadavg 并把系统运行时间和登录用户数一起展示出来$ uptime 14:32:10 up 28 days, 4:12, 2 users, load average: 0.52, 0.58, 0.59它适合作为排查的第一步快速扫一眼负载三值是平稳、上升还是下降。如果 1 分钟值明显高于 15 分钟值说明负载正在爬升反过来说明高峰正在过去。但 uptime 只能给结论不能告诉你是哪个进程导致的所以接下来要上 top。2.2 top / htop交互式进程监控的要点top是 Linux 运维用得最多的命令之一也是定位哪个进程吃资源的首选工具。启动后重点看三块第一块是顶部汇总行。Tasks行里看 running 和 zombie僵尸进程数如果持续增长说明父进程没有正确回收子进程这类问题积累多了会导致 PID 耗尽。%Cpu(s)行里有几个关键列us用户态 CPU 占用业务代码主要在这sy内核态 CPU 占用系统调用、中断处理算在这里waCPU 等待 IO 完成的时间这个值偏高说明磁盘或网络存储是瓶颈st虚拟机被宿主机偷走的 CPU 时间如果你在云主机上看到 st 长期大于 10%说明宿主机的 CPU 超卖严重需要考虑换实例规格。第二块是内存汇总行。KiB Mem 里除了关注 used更要看可用内存 available以及 buff/cache 占了多少。注意 cache 大不等于内存泄漏Linux 会用空闲内存做页缓存应用不够时会自动释放。第三块是进程列表。默认按 CPU 使用率排序按P键可以按 CPU 重新排序按M键按内存排序按1键展示每个 CPU 核心的单独使用率按c键显示完整命令行。进程行的 %CPU 列要特别注意这个值表示进程占用了 CPU 核心的百分比多核下可以超过 100%比如一个进程占满四个核显示就是 400%。htop是 top 的增强版默认用颜色区分 CPU/内存/进程状态支持树状查看进程操作更方便。这个命令通常不会预装# Debian/Ubuntu apt install htop # CentOS/RHEL yum install htop如果对 top 已经很熟了htop 不是必需品但给新同学做分享时 htop 的观感要好很多排查问题时也更直观。2.3 vmstat系统状态快照与 CPU/内存/IO 联动vmstat 是我个人最喜欢的一把瑞士军刀它在一个输出里同时给出进程队列、内存、交换分区、IO、中断和 CPU 使用情况特别适合快速判断瓶颈方向。$ vmstat 1 5 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 2 0 0 1023456 20456 8092345 0 0 12 45 120 356 5 3 90 1 0 3 0 0 1019876 20456 8094567 0 0 0 32 135 420 8 4 88 0 0vmstat 1 5表示每秒采样一次共采样 5 次。判读顺序是r运行队列正在运行和等待 CPU 的进程数。如果 r 经常大于 CPU 核心数说明 CPU 真的繁忙b不可中断睡眠进程数对应前面说的 D 状态这个值持续偏高时优先查磁盘和存储网络si/so每秒从交换分区换入/换出的数据量。这两个值长时间不是 0说明物理内存严重不足系统在靠 swap 硬撑cs上下文切换每秒上下文切换次数。正常的并发服务每秒几千次问题不大如果飙到几万甚至几十万往往是线程数爆炸或者频繁锁竞争wa和 top 的 wa 含义一致IO 繁忙时这个值高。vmstat 的核心价值在于联动。比如r高 us高是 CPU 密集型问题b高 wa高 bo高是磁盘写问题si/so持续非 0 free低是内存问题。一张表就能把三类常见瓶颈区分开非常实用。2.4 mpstat、free、iostat拆解 CPU、内存与磁盘细节mpstat -P ALL 1查看每个 CPU 核心独立的使用率。有时候整体 CPU 不高但某一个核心被单线程任务跑满会导致整体负载偏高且分布不均用这个命令能一眼看出来。free -h看内存。重点看 available 列这是操作系统估算的在不触发 swap 的情况下还能分配给应用的内存比 used 列更有参考价值。我见过很多同学一看 free 列接近 0 就紧张其实 buff/cache 随时可以回收真正要注意的指标是 available 持续低于总量 10%。iostat -x 1看磁盘细节。%util表示设备的繁忙程度接近 100% 意味着磁盘已经饱和await是平均 IO 响应时间机械盘在几十毫秒级正常SSD 应该稳定在几毫秒。如果 SSD 的 await 长期超过 50 毫秒要怀疑磁盘硬件故障或者 RAID 控制器出问题了。另外r_await和w_await通常会分开看写延迟往往比读延迟更接近真实业务感受。3. 负载异常时的倒推排查CPU 高、磁盘 IO 高、SWAP 高分别怎么定位3.1 负载高但 CPU 不高重点盯 vmstat 的 b 列和 iostat 的 await这类问题在挂载了 NFS、CIFS、云盘或使用分布式存储的生产环境里特别常见。典型表现是uptime 显示负载五六七八top 里 CPU 空闲率却很高同时有大量 D 状态进程。排查步骤建议这样走第一步vmstat 1 5看 b 列。如果 b 列持续大于 1 且 wa 较高基本可以断定是存储 IO 问题。第二步iostat -x 1看是哪个设备在扛雷注意这里不光看 %util还要看 await。如果 %util 不高但 await 很高说明设备有大量请求在排队或者访问的是慢速存储路径比如网络文件系统如果 %util 接近 100%那就是磁盘吞吐到上限了。第三步用iotop -o找出具体是哪个进程在疯狂读写。还有一种隐蔽情况负载高、CPU 不高、D 状态进程有但本地磁盘设备看起来没问题这时候要怀疑进程是不是卡在内核锁或者不可中断的同步等待上。可以挑几个 D 状态进程跟踪一下$ for pid in $(ps -eo state,pid | awk $1D{print $2}); do echo PID $pid cat /proc/$pid/stack 2/dev/null | head -20 cat /proc/$pid/status 2/dev/null | grep State done/proc/PID/stack在多数发行版上默认禁止普通用户读取可以换成cat /proc/PID/status至少确认状态。如果确认了是 NFS 卡死一般处理思路是先恢复网络或存储服务再看 NFS 挂载参数是否需要调整比如默认的hard挂载方式遇到底层存储故障时会无限期阻塞业务敏感的目录可以考虑改用soft方式并设置timeo和retrans参数代价是可能产生 IO 错误返回需要应用层做兼容。3.2 负载高且 CPU 高计算密集型问题的进程定位这类问题定位起来相对直接。负载高、us 或 sy 高、vmstat 的 r 列超过核心数说明 CPU 确实是瓶颈。直接top按 P 排序看谁占在最前面就行。但如果占 CPU 的进程是服务进程本身还需要进一步回答它为什么这么忙通常会用到以下工具perf top实时查看 CPU 采样集中在哪些内核函数/用户态函数上能快速发现死循环、热点锁或某段代码异常pidstat -u 1按进程输出 CPU 占用比 top 更适合记录历史数据strace -p PID跟踪进程的系统调用如果是频繁的文件读写、网络发送、内存分配这类系统调用拖高了 sys 占用这个命令一眼就能看出来。实际工作中遇到过一个 Java 服务的 CPU 异常飙升top 显示 java 进程占了 300% 多perf top 定位到是GC线程在疯狂做垃圾回收再往下查是堆内存设置过小导致 Full GC 频次超标。这个案例说明CPU 高只是表象要往下追到为什么高才能找到根因。3.3 内存吃紧导致 SWAP 抖动si/so 与 OOM 风险内存不足的典型信号是free 的 available 持续走低vmstat 的 si 和 so 经常是两位数的增长dmesg 开始出现 OOM killer 日志。这种情况下系统负载也会涨因为内核在频繁换页进程的执行会被拖慢相当于把磁盘当内存用。定位路径是先free -h看 available 还剩多少再用top按 M 排序看哪个进程占内存最多然后用pidstat -r 1观察具体进程的内存变化趋势。如果某个进程内存持续增长且不回落多半是内存泄漏需要配合堆转储、pmap这类工具深查。如果所有进程加起来的内存并不算多但 available 依然很低检查一下是不是 page cache 被某类大文件读取撑着比如频繁读取的日志文件此时调整vm.drop_caches或优化文件读取方式比单纯加内存更对症。关于 swap 还有一个经验值si/so 持续大于几百 KB/s 就要严肃对待了持续几 MB/s 基本等于在用磁盘跑内存业务响应速度会肉眼可见地变慢。这时候临时加 swap 只能缓解一时根本方案还是加内存或者排查内存泄漏。4. 服务器变慢后的完整体检流程按这个顺序执行基本不会漏4.1 排查顺序从全局指标到单点进程我给自己定了一套固定流程遇到业务反馈卡顿或者告警触发时按这个顺序执行基本不会漏掉关键环节步骤命令重点看什么1uptime负载三值判断趋势是上升还是下降2top或htopCPU/内存总览、进程排序、D 状态进程3vmstat 1 5r/b 队列、si/so、wa、us/sy定位瓶颈层面4free -havailable 总量判断内存是否吃紧5iostat -x 1各磁盘 %util、await确认存储路径6dmesg | tail -100内核日志中是否有 OOM、hung task、磁盘或硬件错误7ss -s和ss -tan连接数、TIME_WAIT/CLOSE_WAIT 是否异常8pidstat -d -u -r 1锁定具体进程的 CPU、内存、IO 消耗第 6 步的 dmesg 经常被忽略但它对问题的定性非常有价值。比如看到hung_task_timeout_secs相关的 kernel panic 或者 block 报错基本能锁定是存储内核路径的问题看到Out of memory: Kill process就是内存问题实锤了。很多性能问题不是应用层代码的锅而是底层硬件或内核模块出了问题这一眼就能看出方向。4.2 三个容易被忽略的细节僵尸进程、IO 调度与内核日志排查过程中有三件事新手容易漏但老手几乎每次都会顺手确认。第一僵尸进程。top的 Tasks 行如果 zombie 数量不为 0 且持续增长说明父进程没有调用 wait() 回收子进程。僵尸进程本身不占 CPU 和内存但会占 PID 表项积累到 PID 上限后新进程会 fork 失败。排查时用ps -ef | awk $31 $8~defunct找父进程为 1init/systemd的僵尸这类没人管的僵尸尤其危险。第二IO 调度器。在机械盘时代deadline 和 cfq 调度器的性能差异很显著在 NVMe SSD 上一般用 none即 noop。可以执行cat /sys/block/sda/queue/scheduler查看如果调度器配置不合理在特定混合读写场景下延迟会明显偏高。不过这块现在的内核自动判断能力很强除非确认有调度器引发的性能问题否则不建议频繁修改。第三内核日志里的软锁定。dmesg里如果出现soft lockup或者hard LOCKUP说明有 CPU 长时间被某个内核线程或驱动占用不释放这是比普通用户态 bug 严重得多的问题要优先关注驱动的版本、内核版本是否匹配。4.3 实战案例一次 NFS 挂载失效引发的负载爆炸复盘一次我实际处理过的案例帮助理解上面这套流程怎么落地。某天中午集采系统告警应用服务器负载从平时的 2~3 飙到 35。登上去执行 uptime负载还在涨然后 top 看到大量进程是 D 状态CPU 的 wa 接近 60%。vmstat 的 b 列高达 20 多简直像一张IO 阻塞全家福。iostat 看本地磁盘sda 几乎是空闲的这就很矛盾——本地盘没忙为什么这么多进程在等 IO接着 dmesg 出现了连续的 NFS 相关日志再检查挂载点发现应用目录挂在一个存储节点上而存储节点所在网络已经中断NFS 连接处于假死状态。所有访问挂载目录的进程都卡在等待网络存储响应的 D 状态负载自然被拉爆。处理方式是先强制卸载挂载点恢复应用可用性然后检查网络链路最后把挂载参数从默认的hard,timeo600改成业务可接受的soft,timeo50,retrans2这样存储故障时应用层不会无限等待而是及时收到 IO 错误。当然软挂载意味着数据一致性风险改成软挂载之前一定要确认业务能够容忍读写失败否则宁可在中间加一层高可用存储方案。这个案例最大的教训就是D 状态进程多、负载高、本地磁盘不忙几乎就可以锁定是网络存储或内核阻塞类问题不用纠结应用代码。5. 把监控做成常态定时采集、历史回溯与告警阈值设定5.1 定时采集脚本让监控数据留痕出了问题才登录机器看往往只能看到事后现场。很多负载尖峰持续几分钟就消失等收到告警再登上去指标已经恢复了。所以我一直建议至少要做数据的定时留存把关键指标落盘后面查问题才能有据可依。最简单的方案是用 cron 定时执行采集脚本。比如每 1 分钟记录一次负载和核心指标#!/bin/bash # /usr/local/bin/monitor_collect.sh LOG_DIR/var/log/sysmon mkdir -p $LOG_DIR echo $(date %Y-%m-%d %H:%M:%S) $(uptime) $LOG_DIR/load.log vmstat 1 3 | tail -1 $LOG_DIR/vmstat.log free -h | grep Mem $LOG_DIR/memory.log配合 crontab* * * * * /usr/local/bin/monitor_collect.sh /dev/null 21这个方案不用装任何额外的监控软件几行脚本就能覆盖大部分排障场景。如果觉得脚本维护麻烦装 sysstat 套件后用 sar 更省心——编译好的 sa1/sa2 会定时把系统指标写入 /var/log/sa/saDD 文件默认保留 28 天查询时直接sar -q -f /var/log/sa/saDD就能翻出某一天的历史负载。5.2 sar 历史回溯对比过去才能发现问题sar 是 sysstat 包里的核心命令日常用的几个经典组合# 查看历史负载数据平均负载 sar -q # 查看历史 CPU 使用率 sar -u # 查看历史内存 sar -r # 查看历史网络流量 sar -n DEVsar -q输出的 ldavg-1、ldavg-5、ldavg-15 可以和系统当时的运行状态对起来看。比如你可以用sar -q -s 13:00:00 -e 14:00:00精确看午饭前后那段高峰的负载变化再配合sar -u和sar -r判断当时是 CPU 忙、内存紧还是 IO 慢。我自己的习惯是每周巡检时翻一遍本周的 sar 数据重点关注负载是不是在固定时间点规律性升高、内存 available 是不是每天都在缓慢下降、swap 的 si/so 有没有出现过非 0 值。这些长期趋势靠肉眼看 uptime 是永远发现不了的。5.3 告警阈值怎么定基于核心数与业务模型监控数据有了下一步是告警。阈值定得太松会漏报太紧会天天被噪音轰炸我的经验是先按机器配置给一个机器级默认值再根据业务表现逐步调整指标建议阈值说明load average核心数 x 0.7 警告核心数 x 1.0 告警按 nproc 计算持续 5 分钟以上触发CPU 空闲率低于 20% 关注低于 10% 告警用 idle 列判断内存 available低于总量 20% 关注低于 10% 告警看 available不是 used磁盘 %util高于 85% 且持续 5 分钟注意 SSD 和 HDD 的差异swap si/so单次采样超过 500KB/s持续出现就要查内存上下文切换 cs单核每秒超过 5 万次仅作参考需要结合业务D 状态进程数持续大于 1且伴随 wa 升高结合 iostat 一起判断这些阈值不是死标准。比如一台只在白天有业务的机器深夜负载高很可能是定时任务导致的直接告警意义不大一台跑批处理任务的机器负载长期在核心数 1.2 倍左右可能也是正常的。所以告警阈值上线后至少要观察一到两周根据实际误报和漏报情况做一轮调整。我见过不少团队首版阈值定得过严结果告警频繁到大家都麻木了最后真正出问题反而没人响应——监控系统最怕的不是不报警而是狼来了太多次。另外多说一句上面所有命令和阈值都建议先在自己的开发机上实测一轮。比如你在机器上执行stress --cpu 4 --timeout 60人为制造 CPU 压力然后同时观察 uptime、top、vmstat 的变化亲眼看到负载从 1.0 飙到 4.0 再回落比死记任何判读标准都管用。性能监控这件事工具命令就好比体检仪器会用只是第一步能读懂指标背后的含义并能把它和业务现象关联起来才算真正掌握。