凌晨三点,告警群弹出来:线上 API 服务器 CPU 飙到 100%,load average 破 50。你迷迷糊糊登录上去 top 一看,CPU 使用率并不高,但服务器响应极慢。这时候第一反应如果是 reboot,大概率要背锅。2026 年做运维,不能只会重启和看 top,得把底层原理吃透。下面按几个高频故障场景拆解。
1. Load average 不是 CPU 使用率,是进程排队数
很多人把 load average 当成 CPU 占用率,这是最大的误区。load average 统计的是处于可运行状态(R)和不可中断睡眠(D)的进程数。8 核机器 load 持续超过 8,说明有任务在排队;如果 CPU 使用率不高但 load 很高,基本全是 D 状态进程在等 IO。
上个月我们一个 NFS 挂载点抖动,API 机器 load 涨到 30 多,但 CPU 才 10%。用 ps -eo stat,pid,cmd | grep '^D' 一把揪出一堆卡在 nfs_write 的进程。立刻切流量到备用节点,再查存储侧,避免了无意义重启。实操建议:先跑 vmstat 1 看 r 列和 b 列,r 高才是 CPU 瓶颈,b 高就是 IO 堵了;再配合 pidstat -d 1 看哪个 PID 在狂读盘。工具推荐 atop,打开后按 D 直接显示 D 状态进程,比 top 好用太多。
2. OOM Killer 不是随机杀人,它有自己的一套“坏蛋评分”
内存耗尽时,内核 OOM Killer 会根据 oom_score 选一个进程杀掉,这个分数是动态计算的,主要看进程占了多少内存,再叠加 oom_score_adj 调整值。很多人以为 OOM 是随机杀,其实杀的基本是内存占用大户。
我们 K8s 节点上 MySQL 曾经一扩容数据量就被 OOM 杀,日志里 oom_score 高达 900 多。解决办法不是加内存,而是给核心服务设置保护:echo -1000 > /proc/$(pidof mysqld)/oom_score_adj。这个值范围 -1000 到 1000,-1000 表示基本不会被杀,1000 表示第一时间杀掉。容器里可以用 --oom-score-adj 参数设置。做完之后 MySQL 再也没因为 OOM 挂过,但机器上其他非核心进程被杀的次数增加了,这是正常的取舍。
3. 磁盘慢不慢,别只盯 iowait
iowait 是最容易被误读的指标。它只代表 CPU 空闲且在等 IO 的时间占比,多核机器上只要有一个核在忙,iowait 就可能很低,但磁盘已经满负载了。判断磁盘瓶颈要看 iostat -x 1 里的 await 和 %util。
我们一次压测发现服务响应突然变慢,%util 才 60%,但 await 到了 200ms。后来用 fio --name=test --filename=/tmp/testfile --rw=randwrite --bs=4k --size=1G --iodepth=32 --runtime=60 --time_based 测出来云盘随机写只有 1500 IOPS,远低于标称值,原因是云盘有 IOPS 限制。实操建议:先用 fio 拿到真实基线,再用 blktrace 看延迟分布,最后配合 cgroup v2 的 io.max 给数据库限制 IO,避免一个慢盘拖垮整台机器。
4. TCP 连接建立不上,先数一下内核队列有没有溢出
高并发服务最怕连接建立失败。TCP 三次握手有两个队列:半连接队列和全连接队列。如果应用 accept 太慢,全连接队列会满,新连接直接丢包。很多人上来就调大文件描述符,结果没用,因为根本不是 fd 不够。
某网关服务压测时每秒有几万新建连接,客户端报 timeout。跑到服务器上执行 ss -lnt,发现 80 端口 Recv-Q 长期大于 0,说明 accept 队列满了。解决办法:把 net.core.somaxconn 从默认 128 调到 1024,同时应用层 listen 的 backlog 也要改,比如 Nginx 的 backlog=1024。还要打开 net.ipv4.tcp_syncookies=1 防半连接攻击。调完后握手失败率从 3.2% 降到 0.1%。另外 ss -s 可以看到 timewait 和 overflow 计数,建议每次压测先看这个。
5. eBPF 把服务器变成白盒,别再靠猜
2026 年如果还不会用 eBPF,排查问题就像蒙着眼睛修车。eBPF 可以在内核态安全地挂载探针,实时统计系统调用、调度延迟、磁盘 IO 分布。我最常用的两个工具是 bcc-tools 里的 runqlat 和 biolatency。
一台 Java 服务频繁超时,监控显示 CPU 并不高。用 runqlat 10 看了 10 秒调度延迟分布,发现 P99 到了 30ms,说明线程等 CPU 排队严重。进一步用 bpftrace -e 'tracepoint:sched:sched_wakeup { @[comm] = count(); }' 统计唤醒次数,发现是线程池竞争导致大量线程唤醒后排队。后来调整线程池大小并绑定 CPU,超时率降了 70%。建议至少装一套 bcc-tools,再配 Grafana 的 eBPF 插件做长期监控。工具不在多,把 runqlat 和 biolatency 用熟,能解决一半以上“玄学”延迟问题。
这五个场景只是服务器运维底层原理的冰山一角,但都有个共同点:别再只看表面指标,也别一上来就重启。把内核怎么调度、怎么杀进程、怎么排队搞清楚,排障速度会快一个量级。
📌 延伸阅读:新手必看:百度搜索资源平台使用的完整指南 | 2026网站推广:流量红利彻底见底,我只押这4条反常识打法
码字不易,如果觉得有用,欢迎分享给更多需要的朋友。你的支持是我们持续更新的动力。