面试官问:"服务变慢了,你第一步看什么?"
答"看 CPU、内存"不算错,但真把你放到一台出事的机器前,第一眼多半会先懵一下:
uptime
# 15:04:01 up 42 days, load average: 23.87, 22.14, 19.60
top
# %Cpu(s): 4.2 us, 1.1 sy, 0.3 ni, 93.5 id, 0.6 wa ...
load 23,CPU 却有 93% 闲着。机器卡得要死,可它明明没在干活。
这道题几乎是运维面试的分水岭。能把这两个数字为什么能同时出现讲清楚的人,和只会背"load 高就是负载高"的人,不是一个段位。
load 数的根本不是 CPU
绝大多数人第一次都理解错:以为 load average 是 CPU 使用率的另一种写法。不是。
在 Linux 上,load average 数的是处于两种状态的进程数的移动平均:
- D:不可中断睡眠——卡在内核里等一个 I/O 完成,比如等磁盘、等 NFS 返回。
关键就在这个 D。它不占 CPU,但被算进 load。
这就解释了开头那两个数字:CPU 空着,是因为二十多个进程根本不在抢 CPU,它们全卡在 D 状态等 I/O。CPU 想干活,可数据还没从磁盘上来,只能干等。这台机器不是算力不够,是 I/O 堵死了。 load 是"有多少进程在排队等资源",磁盘也是资源。
传统 Unix 的 load 只数 R,是纯 CPU 视角。Linux 特意把 D 塞了进去,就是为了让这一个数字能同时反映出 I/O 压力——代价是你必须知道它数了 D,否则永远看不懂"CPU 闲着 load 却爆表"。
那个 kill -9 都杀不掉的 D
顺着 D 往下挖,是另一道高频题:为什么有的进程 kill -9 都杀不掉?
kill -9 发的是 SIGKILL,号称无法被捕获、无法被忽略。但它有个前提没人明说:信号是在进程即将从内核态返回用户态的那一刻才被检查处理的。
D 状态的进程正卡在内核深处等 I/O,压根回不到那个检查点。你的 SIGKILL 不是被无视了,是在排队——排到进程从内核出来才能生效。可要是它等的那块磁盘坏了、那台 NFS 服务器宕了,它就永远出不来,信号也就永远送不到。进程像焊死在那儿,ps 一看状态是 D,怎么都弄不走。
为什么内核要设计成"不可中断"?因为这时候进程手里攥着内核态的数据结构(比如某个正在读写的磁盘缓冲),半道被信号打断,这些结构会处于不一致状态,轻则数据损坏,重则内核崩溃。内核宁可让它卡死,也不敢让它带着半拉子状态跑路。
所以碰到杀不掉的 D 进程,方向根本不在这个进程身上——去查它等的那个 I/O:ls 卡在某个挂载点就是存储的事,一堆进程一起 D 住多半是共享存储或 NFS 出了问题。硬碰硬地 kill 是白费劲。
顺着一个 load,能把半个系统摸一遍
load 高只是告诉你"有东西在排队",接下来的分诊,恰好把运维要懂的几块串成了一条线:
vmstat 1
# procs -----------memory---------- ---swap-- ... ----cpu----
# r b swpd free buff cache si so us sy id wa
# 1 22 0 194352 ... 0 0 4 1 71 24
r 是等 CPU 的,b 是卡在 D 状态(等 I/O)的。这里 b 是 22,跟 load 对上了——问题在 I/O,不在 CPU。
I/O 慢,再往下拆:
iostat -x 1
# Device r/s w/s ... await %util
# sda 12 340 890 99.7
%util 贴着 100,await(每次 I/O 平均等待)从正常的几毫秒飙到 890 毫秒——磁盘已经饱和,所有读写在排长队。到这一步,故障从"服务慢"收窄成了"某块盘扛不住了",接下来才是看谁在疯狂读写、是不是慢查询在扫全表、要不要限流。
同一个 load,如果 si/so(swap 换入换出)不是 0,方向就拐到内存不够、在拿磁盘当内存用;如果 b 很小而 r 很大,才是真的 CPU 不够。一个入口,顺着往下,进程状态、CPU、I/O、内存、swap 全过了一遍。 这就是有排障框架和瞎敲命令的区别——不是命令记得多,是知道下一步该往哪拐。
load 也会骗你
但只记住"load 高 = 有问题",照样会栽,因为它有边界。
它是绝对数,不看你有几个核。 load 8 在单核机上是过载 8 倍,在 8 核机上刚好满载,在 32 核机上闲得很。脱离核数谈 load 高低没有意义,得先 nproc 看有几个核。
在容器里它基本是假的。 容器里 uptime 读到的 /proc/loadavg 是宿主机的整机负载,不是你这个容器的。宿主机上挤了几十个容器,你在自己容器里看到 load 高得吓人,其实跟你没关系。这个坑在云原生环境里天天有人踩。
知道这条边界,比死记"load 高"有用得多:看到一个数字先问它到底数的是什么、在什么口径下成立——这恰恰是面试官想筛出来的思维方式。
剩下那几块,也是同一个套路
运维要懂的其他东西,本质都是这一招:别停在命令,往下多问一层为什么。
- TIME_WAIT 堆了几万个:不是 bug,是主动关连接的一方必经的 2×MSL(默认 60 秒)等待,为的是让迟到的包别串到新连接上。高并发短连接下堆积成瓶颈,开
tcp_tw_reuse 缓解——但老教程让你开的 tcp_tw_recycle,在 NAT 环境(云主机、家宽都算)会直接丢包,越改越坏。 - Nginx 502 vs 504:502 是后端根本没接上(进程挂了/没监听),504 是后端接上了但超时没回。一个查存活,一个查慢在哪,方向完全不同。
- 磁盘还剩几十 G 却写不进文件:
df -h 正常,df -i 一看 inode 满了——ext4 的 inode 数量是格式化时就定死、事后加不了的,被海量小文件占满,空间再多也创不出新文件。 - MySQL 突然拖垮整站:
show processlist 抓当前查询,一条没走索引的 SQL 在几百万行的表上做全表扫描,就能把连接池占满。
每一条,会命令的人一抓一大把,能说清楚下面那层机制的,面试里一开口就不一样。
没有生产环境,这些全能自己造
题主在机房没碰过核心业务,觉得吃亏。但反过来看,你手里有别人没有的东西:有机器、有时间、还没有搞挂了要担责的压力。
上面这些现象,没一个需要等生产事故——全能自己复现:
stress-ng --io 8 --hdd 4 --timeout 60s # 把 I/O 打满,另开窗口看 load 和 vmstat 的 b 列怎么涨
dd if=/dev/zero of=/tmp/fill bs=1M # 把盘写满,看服务报什么错
stress-ng 想压哪类资源都行(CPU、内存、I/O 各来一种),dd 灌满磁盘,把 Nginx 的 upstream 指向一个死 IP 看 502/504 的日志差别,sysbench 给 MySQL 加压再去抓慢查询。再拿本地虚机搭一套 Prometheus + Grafana,把这些指标收上来自己看波形。一个下午,开头那台"load 23、CPU 却闲着"的机器,你就能亲手造出来、亲眼看它怎么恢复。
面试时说"我把 I/O 压满过,看着 load 涨上去、vmstat 的 b 列跟着飙",和"我知道 load 高说明负载高",是两种完全不同的分量。
JD 上的"3年经验"多半是溢价。运维招人真正怕的,是出了事站在机器前不知道从哪下手的人——不是不会命令,是脑子里没有那张"哪个数字对应哪一层、下一步往哪拐"的地图。
Nginx、MySQL、网络、Shell,每一块都别停在"会敲",多挖一层"为什么",就已经甩开大半只会背题的人了。
现在把报错日志直接丢给 AI,它连 load 高、D 状态这种都能分析个八九不离十。那对刚入行的人来说,这套从现象挖到内核的功夫,是更该练了,还是可以交给 AI、人只管判断和执行就行?你怎么看。