做运维的朋友应该都经历过这样的场景:半夜手机突然震动,监控告警提示某台服务器的负载均值超过了阈值。你睡眼惺忪爬起来,登录服务器敲了一句uptime,看到 load average 后面跟着 37.62、28.15、19.83 三个数字,瞬间清醒一半——这怕不是要出大事了?然后你下意识看了一眼 CPU 核数,发现是台 64 核的机器,又稍微松了口气。这种“看到负载高就慌,看到核数多又不知道该怎么判断”的状态,几乎是每个运维新人都要迈过的一道坎。
CPU 平均负载(load average)是 Linux 系统里最基础、也最容易误读的性能指标之一。它不是 CPU 使用率,也不是内存占用率,它反映的是系统在一段时间内的“排队情况”。这篇文章我会从负载均值的定义说起,把三个数字到底什么意思讲清楚,再结合我实际排查过的故障场景,把负载飙升后从定位到解决的完整思路走一遍。适合刚入门运维、或者对负载概念一直似懂非懂的人看,看完你至少能在下次收到告警时心里有个底,知道先看什么、再看什么、什么时候该报警。
1. 负载均值到底是什么
1.1 三个数字,三段历史
先看最直观的东西。登录任意一台 Linux 机器,执行:
$ uptime 10:24:12 up 87 days, 3:12, 2 users, load average: 0.37, 0.42, 0.31最后一行就是负载均值,三个数字从左到右分别代表过去 1 分钟、5 分钟、15 分钟的平均负载。注意“平均”两个字,它不是一个瞬时快照,而是一个时间窗口内的统计结果。所以很多时候我们看负载,更像是在看一段“历史趋势”:1 分钟的值代表当前正在发生什么,5 分钟的值代表最近一小段时间的整体情况,15 分钟的值代表更长周期里的基线水平。
这三个数字的关系非常有意思。如果 1 分钟值明显高于 15 分钟值,说明负载正在快速上涨,问题可能刚刚发生;如果 1 分钟值低于 15 分钟值,说明高峰期已经过去,系统正在恢复;如果三个数差不多,说明系统处于一个相对稳定的状态。我后来排查问题,基本第一件事就是看这三个数的走势,它比任何监控图都来得直白。
1.2 R 状态与 D 状态:负载统计里的两个来源
那负载均值统计的到底是什么?简单说,是 Linux 内核运行队列里的“任务数量”。具体包含两类进程:
- R 状态(Running/Runnable):进程在内存里,随时可以运行,要么正在被 CPU 执行,要么在排队等着 CPU。
- D 状态(Uninterruptible Sleep):进程在等待某种内核操作完成,通常是磁盘 IO、网络 IO,或者等内存页换入(如 kswapd),这个状态不能被打断,只能等它自己完成。
注意,负载的统计把 R 和 D 状态的进程都算进去了。这一点特别关键,也是很多人误读负载的根源。很多新手以为负载高就等于 CPU 忙,其实不完全对。如果一堆进程卡在磁盘 IO 上,CPU 可能一直闲着,但负载照样能冲到很高,因为那些 D 状态进程全在队列里挂着。
1.3 内核是怎么算出这个数的
关于计算细节,不需要背公式,但了解一下能帮你理解为什么负载是“平滑”的。内核不会每秒精确计算一次,而是每隔 5 秒(LOAD_FREQ)采样一次当前运行队列里可运行任务数加不可中断任务数的总和,然后做指数衰减加权平均。简单理解就是:越靠近当前时刻的样本权重越大,很久以前的样本会被逐步“遗忘”,所以负载曲线是平滑的,不会突然跳一个离谱的尖峰。
有人可能会问,为什么采样间隔是 5 秒?这是内核多年演化下来的经验值,兼顾了统计准确性和系统开销。如果你在监控系统里看到负载曲线非常平滑,就是这个机制在起作用。理解了这一点,你就明白为什么负载均值不适合用来捕捉“毫秒级抖动”,它更擅长反映系统过去 1 到 15 分钟的宏观压力。
2. 负载与CPU使用率:一对容易混淆的孪生指标
2.1 一个是“时间占比”,一个是“排队人数”
CPU 使用率描述的是 CPU 在一段时间里“有多忙”,单位是百分比,本质是时间占比。比如一个核在 1 秒内有 0.8 秒在执行指令,那这个核的使用率就是 80%。而负载均值描述的是“有多少任务在等待被处理”,近似理解为排队人数或队列长度,它没有单位。
用银行柜台来类比:CPU 核就是柜员,使用率是柜员在某一小时内实际办理业务的时间比例,负载则是大厅里排队等号的人数。柜员可能从上班一直忙到下班(使用率 100%),但每笔业务都办得很快,排队的人反而不多;也有可能柜员有一半时间在发呆(使用率 50%),但队伍排到了门口,因为前面那笔业务要调资料、等审批,卡住了。第二种情况对应到服务器,就是常见的“负载高但 CPU 使用率不高”,罪魁祸首往往是 D 状态进程占住了队列位置。
2.2 四种组合怎么解读
把负载和 CPU 使用率组合起来看,会出现四种典型情况:
| 负载均值 | CPU使用率 | 大概率原因 | 处理方向 |
|---|---|---|---|
| 高 | 高 | 计算密集型任务过多,CPU 真的不够用 | 优化代码、限制并发、扩容 CPU |
| 高 | 低 | 大量 D 状态进程在等 IO、等锁或内存压力 | 查磁盘、网络存储、内存和 swap |
| 低 | 高 | 任务处理速度快,队列来不及堆积 | 暂时不用管,继续观察 |
| 低 | 低 | 系统空闲或负载本身不高 | 不需要处理 |
这四种组合里,第二种最容易被经验和直觉误导。我刚开始干运维时,见到负载高就下意识去查 CPU,结果 top 一看每个核心都很闲,瞬间就懵了。后来才搞明白,负载队列里排队的可能根本不是在等 CPU,而是在等 IO。所以排查的第一步不是“看 CPU”,而是先分清负载到底是被谁拉起来的。
2.3 为什么“CPU 使用率 100%”不代表系统扛不住
再补充一个容易混淆的点:单核机器的 CPU 使用率 100% 意味着这个核已经满载,此时负载如果是 1.0,那正好是“一个任务在跑”的饱和状态。但对一台 64 核的机器来说,负载 1.0 简直是小菜一碟,系统还远没到极限。很多刚入门的同学看到监控面板上某个核的使用率 100% 就紧张,实际上只要整体负载没有超过核数,系统的响应能力就是够的。反过来,哪怕 CPU 使用率只有 40%,如果负载已经超过核数的好几倍,那说明任务在堆积,用户体验会明显变卡。
3. 负载多高才算高:判断准则与常见误区
3.1 永远别脱离核数谈负载
判断负载是否异常的第一原则是看 CPU 逻辑核数。负载 4.0 放到单核机器上是灭顶之灾,放到 8 核机器上可能只是稍微忙一点。先不说哲学问题,直接给一个我用下来还算顺手的经验区间,以“负载 ÷ 核数”为判断基准:
- 比值小于 0.7:系统状态还算健康,队列基本通畅。
- 比值在 0.7 到 1.0 之间:开始紧张,值得关注,该看看是什么任务在增长。
- 比值大于 1.0:队列开始积压,CPU 或 IO 资源不够用了,需要介入。
注意这只是经验值,不是铁律。有些核心业务系统要求极低延迟,负载刚超过核数的一半就得告警;有些批处理服务器跑的就是密集计算任务,负载常年是核数的两三倍也没人管。判断标准要结合业务容忍度,但新手可以先按这个区间来。
看核数的命令很简单:
$ nproc 16 $ lscpu | grep -E '^CPU\(s\)|^Thread|^Core|^Socket'这两个命令输出的逻辑核数量就是我们算比值时的那个“核数”。
3.2 三个时间窗口的趋势才是重点
比绝对数值更重要的是趋势。负载均值有三个时间窗口,不去比较它们等于浪费了一半信息。分享几个我实测下来比较有代表性的形态:
- 1 分钟值突然飙升,5 分钟和 15 分钟值还在低位:说明是刚发生的瞬时抖动,可能是杀掉大任务、启动批量任务或某个进程突然开始狂算。先观察几分钟,如果 1 分钟值回落,基本不用管。
- 三个值同步高位运行:这种最麻烦,说明问题已经持续了至少 15 分钟以上,而且还在恶化。这时候别犹豫,马上进入排查流程。
- 1 分钟值低于 15 分钟值:说明高峰已经过去,系统正在恢复。你甚至可以不急着处理,先确认刚才发生了什么。
有一次我排查一个晚上的告警,第一眼看到 1 分钟负载 5.8、5 分钟负载 12.4、15 分钟负载 18.9,数值在掉头向下,就知道高峰已经过了,后面慢慢定位原因就行,完全不用大半夜把进程乱杀一通。要根据三个值的相对关系做判断,而不是看到第一个数字高了就慌。
3.3 内存压力抬起来的高负载,最容易被误判
有个场景我印象特别深:某台 4 核机器负载到了 8,看起来已经超载,我按常规思路查 CPU 和 IO,都没找到明显问题。后来看内存才发现,内存已经耗尽,系统一直在触发 swap,kswapd 进程大量处于 D 状态,全堵在队列里,load 自然就上去了。
内存压力会通过 page cache 回收、swap 换页这些动作抬升负载,而 CPU 使用率看起来并不高,因为真正排队的是“等内存相关的内核任务”,不是“等 CPU 的任务”。所以排查高负载时,内存指标一定要同时看,尤其是free -h里的 available 和 swap 使用量。如果 available 很低、swap 持续增长,那负载高涨的原因很可能在内存而不是在 CPU。
$ free -h total used free shared buff/cache available Mem: 15Gi 8.2Gi 1.1Gi 112Mi 6.1Gi 5.9Gi Swap: 2.0Gi 1.2Gi 812Miavailable 这项是真正可分配给新进程的内存,比 free 列更有参考价值。看到 swap 有大量占用,基本可以怀疑内存压力拖高了负载。
4. 负载飙升后的完整排查流程
4.1 第一眼:uptime 和 top 一起看
收到负载告警后,我习惯先把uptime和top一起跑。uptime看三值走势,top看实时进程和 CPU 状态。不要小看这一步,它能帮你快速把问题分成两类:CPU 密集还是 IO 密集。
$ top -d 5 top - 10:30:01 up 87 days, 3:18, 2 users, load average: 8.27, 7.44, 6.21 Tasks: 320 total, 2 running, 318 sleeping, 0 stopped, 0 zombie %Cpu(s): 12.5 us, 3.1 sy, 0.0 ni, 78.1 id, 6.3 wa, 0.0 hi, 0.0 si, 0.0 st重点看%Cpu(s)那行里的 us(用户态)、sy(内核态)、wa(等待 IO)和 id(空闲)。如果 wa 很高而 id 很低甚至趋近于 0,那是典型的 IO 等待型高负载;如果 us 或 sy 很高,那是计算型。这一步直接决定了你后面往哪个方向查。
4.2 第二手:vmstat 区分 CPU 饱和还是 IO 瓶颈
top 看到的是当前快照,vmstat能看到更连续的内核状态。我一般这么跑:
$ 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 3 205376 158336 42160 6021120 120 180 3240 2108 1023 3300 18 6 56 20 0 7 2 205376 160256 42160 6021504 110 170 3210 2014 1018 3250 17 6 58 19 0新手重点看两个字段:r和b。r是运行队列里可运行任务数,b是阻塞在不可中断状态的任务数。
- 如果
r持续大于核数,说明 CPU 确实不够用。 - 如果
b居高不下,同时wa偏高,说明大量进程在等 IO。 - 如果
si/so(swap in/out)不为零且持续发生,说明内存压力很大,配合上一节的方法去查内存。
vmstat的价值在于用数字把 top 的画面“定量化”,帮你判断问题到底属于 CPU 饱和、IO 阻塞还是内存换页,这三个方向的后继操作完全不同。
4.3 第三招:pidstat 锁定具体进程
确认方向后,就要把罪魁祸首揪出来。pidstat是 sysstat 包里的工具,非常适合按进程维度看 CPU、IO 和上下文切换。
# 查看各进程的 CPU 使用率 $ pidstat -u 1 5 # 查看各进程的 IO 等待和读写耗时 $ pidstat -d 1 5如果是 CPU 型高负载,pidstat -u的输出里会有某几个进程的 %CPU 明显异常,甚至超过 100%(多线程进程占用多个核)。如果是 IO 型,pidstat -d里await和读写量会很突出。定位到具体 PID 后,再用ps看它的启动命令、启动时间、所属用户,判断是业务进程还是异常进程。
$ ps -ef | grep <PID> | grep -v grep很多隐藏问题到这里就水落石出了,比如某个日志清理脚本循环空转、某个应用连接的线程数爆炸、或者一个被人塞进来的挖矿进程在偷跑。之前遇到过一次负载异常,就是靠pidstat -u发现一个陌生进程的 CPU 长时间占用 300%,查了启动命令才发现是被入侵植入了挖矿程序。
4.4 深入线程层:perf top 和线程 dump
定位到进程后,如果还想往里挖,可以用top -H -p PID看该进程下各线程的 CPU 占用,看看是不是所有压力都集中在一个线程上。对于 Java 应用,配合jstack导线程栈就能看到具体在跑什么代码;对于其他语言,perf top -p PID可以采样看热点函数在哪。
$ top -H -p 12345 $ perf top -p 12345这一层不需要每次都用,通常到pidstat已经能解决 80% 的问题。但如果问题反复出现、你怀疑是代码内部的死循环或锁竞争,那线程视角就是下一步必须做的事。我的经验是:一个高负载问题从告警到定位出具体代码位置,最常见的时间消耗反而在前两层判断上,多花一分钟分清 CPU 型和 IO 型,后面能省一个小时。
5. 三个典型故障场景复盘与速查表
5.1 场景一:日志进程把 CPU 吃满,看着像“正常”的重度计算
有次某业务服务器的负载从 1.0 一路涨到 12,top 里看到某个日志采集进程 CPU 占用超过 200%。第一反应是加机器,但用pidstat -u细看后发现这个进程在疯狂重试连接远程日志服务,每次连接失败就进入短暂忙等,然后又失败又忙等,形成死循环。表面上它是“在干活”,实际上是在做无意义的重试。
处理思路是限制重试频率、增加退避算法,并且把远程存储的存活检测独立出来。很多 CPU 型高负载的根因不是计算量大,而是程序在错误路径上的自旋和重试。排查时不要只看 CPU 占用高不高,还要看这个进程“忙得有没有价值”。这也是为什么我强烈建议定位到进程之后,去看一眼它的日志和工作状态,而不是直接 kill。
5.2 场景二:磁盘 IO 卡死,负载虚高背后的 D 状态长队
另一个经典场景:负载高得吓人,top 里却找不到 CPU 占用高的进程,每个核都很闲。这时vmstat的b列大概率很高,wa列明显上涨。再往下用pidstat -d一看,一堆进程在读写同一个磁盘分区,await数值大得离谱。这种问题多半出在磁盘本身或挂载的网络存储上,比如磁盘坏道导致反复重试、NFS 服务端卡住导致客户端进程集体阻塞。
处理这类问题,第一件事是确认哪些进程在等 IO,能用lsblk和iostat -x 1看物理磁盘的利用率。iostat里%util接近 100% 说明盘已经跑满,await很高说明单次 IO 耗时太长。如果是网络存储,还要看挂载点的健康状态。这种场景下盲目重启进程没用,因为根因在存储层,重启完它们还是会堵在同一批 IO 上。
5.3 场景三:找不到元凶的“幽灵负载”
最让人头疼的是那种负载高、但所有常规指标都看起来正常的“幽灵负载”。进程列表里没有高 CPU 进程,磁盘 IO 正常,内存也还够用,但负载就是压不下去。这时候要往几个容易被忽略的方向想:
- 内存压力:上面说的 page cache 回收和 kswapd,可能
free看起来还行,但 swap 分区的状态一直在波动。 - 大量小文件 IO:单个进程 IO 不突出,但几百个进程同时做小规模读写,累积起来也能把负载顶上去,
pidstat -d放大采样窗口才能看出来。 - 内核锁竞争和上下文切换:
vmstat里cs(上下文切换)高得离谱,这种情况看单个进程的 CPU 往往都不高,但系统整体吞吐已经因为频繁切换而崩了。
遇到幽灵负载,我会把采样拉长到十分钟,同时把vmstat 1 10、pidstat -d 1 10、sar -q 1 10三组数据一起看,让数据说话。运维排查最忌凭感觉拍脑袋,数据不足的时候就先等数据,不要急着下结论。
5.4 常见问题速查表
| 现象 | 初步判断 | 用到的命令 | 后续动作 |
|---|---|---|---|
| 负载高,us/sy 高 | CPU 型 | top、pidstat -u | 定位进程,优化或扩容 |
| 负载高,wa 高,b 列高 | IO 型 | vmstat、iostat -x、pidstat -d | 查磁盘、存储、慢 IO |
| 负载高,swap 持续增长 | 内存压力 | free -h、vmstat的 si/so | 优化内存,检查泄漏,扩容 |
| 负载高,常规指标全正常 | 综合/隐蔽因素 | sar -q、长时采样、perf top | 排查锁竞争、小 IO、内核问题 |
这张表是我平时排查的默认起点,按表里的思路走一遍,大多数负载问题都能有一个明确的下一步。新手可以把这张表打印出来贴在工位旁边,比背命令高效得多。
我个人这些年的体会是:负载均值这个指标,最怕的不是“高”,而是“看不懂为什么高”。三个数字是趋势,R 和 D 状态是根因,核数是坐标系,这四件事想明白了,负载问题就成功了一大半。最后再分享一个实用小习惯:我每排查一个负载问题,都会把当时的uptime、top、vmstat输出保存下来,标注好业务场景和最终根因。积累几个月后回头看,很多问题模式其实是重复的,排查速度会快非常多。运维这个活,说到底就是见得多、记得住、下次不慌。