导语: 今天,我们来聊聊当服务器性能出现瓶颈时,如何建立一套标准、高效、无死角的排查方法论,这不仅是一篇干货,更是你深夜保命的 SRE 生存指南。
一、告别无头苍蝇式排查
很多运维兄弟排查问题时,上来就敲个 top,看着一堆跳动的数字发呆,真正的老鸟,会利用性能调优大师 Brendan Gregg 提出的USE(Utilization 使用率、Saturation 饱和度、Errors 错误)方法论,建立对系统的宏观认知。
别急着下潜,先在水面上看看全貌,请按顺序敲下这几个命令:
1. uptime:
uptime
16:20:15 up 45 days, 12:00, 1 users, load average: 45.15, 20.05, 10.12
重点看最后三个数字(1分钟、5分钟、15分钟的平均负载),如果 1 分钟的负载远高于 15 分钟,说明是突发流量;如果 15 分钟负载一直很高,说明系统长期处于高压状态。
避坑指南: Load Average 不等于 CPU 使用率,在 Linux 中,处于不可中断睡眠状态(比如等待磁盘 I/O)的进程也会被计入 Load,Load 极高但 CPU 空闲时,去查磁盘。
2. dmesg | tail:
内核在遇到致命问题时一定会发牢骚,这里是你发现底层故障的捷径。看到 Out of memory 说明内存爆了;看到 TCP: time wait bucket table overflow 说明网络连接数过多。
3. vmstat 1 3:
这个命令每秒输出一次系统的整体健康度,连续输出 3 次,包含进程、内存、I/O 和 CPU 的核心指标。
vmstat 1 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
8 0 0 123456 45678 890123 0 0 0 0 1200 900 85 15 0 0 0
vmstat 1 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 3 0 120000 45678 890123 0 0 50000 0 3000 4500 5 10 15 70 0
- 看第一部分(CPU瓶颈):
r(运行队列)高达 8,而你可能只有 4 核 CPU,同时 us(用户态)高达 85%,id(空闲)为 0。结论:典型的 CPU 瓶颈,业务代码把 CPU 打满了。 - 看第二部分(I/O瓶颈):
b(阻塞队列)变成了 3,同时 wa(I/O 等待)飙升到了 70%,bi(块设备读取)非常高。结论:典型的磁盘 I/O 瓶颈,进程都在排队等物理磁盘读写,CPU 其实很闲。
二、四大子系统微观剖析
1. CPU 飙高排查思路
假设你通过 vmstat 发现 us(用户态 CPU)高达 90%。
- 找进程: 用
top 按 P 排序,或 pidstat -u 1,找到霸占 CPU 的 PID(假设为 10086)。 - 找线程: 执行
top -H -p 10086,找到占用最高的特定线程 TID,用于去 Java 的 jstack 等日志里对应代码行。 - 看调用栈: 如果代码不是你写的,直接用
perf top -p 10086 看它在干嘛。
perf top -p 10086
Overhead Shared Object Symbol
45.20% libjvm.so [.] GCTaskThread::run()
20.15% [kernel] [k] _raw_spin_unlock_irqrestore
深度解析: 如果你看到排名第一的是 GCTaskThread::run(),说明 Java 正在疯狂进行垃圾回收(Full GC),这就是典型的“内存泄漏导致频繁 GC,进而把 CPU 打满”的连环案,排查方向立刻转向 JVM 堆内存。
2. 内存泄漏与 OOM 现场取证
- 辨别真假内存不足: 执行
free -h,新手看到 free 剩下几十 MB 就会慌,其实真正判断应用可用内存的是 available 列,如果 available 见底,那才是真的危急。 - 找吃内存大户:
top 按 M 排序,重点看 RES(常驻内存),而不是 VIRT(虚拟内存)。 - OOM 死亡回放: 去
/var/log/messages 搜 Killed process。
3. 磁盘 I/O 瓶颈:看不见的慢动作
I/O 问题最恶心,会拖慢整个系统,表现出玄学卡顿,直接掏出神器 iostat -xz 1:
iostat -xz 1
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
vda 0.00 5.00 150.00 200.00 4096.00 8192.00 70.15 5.50 125.00 80.00 150.00 2.85 100.00%
- 致命的 100%: 看到最后面的
%util 达到 100.00% 了吗?这意味着这一秒钟内,磁盘彻底被榨干。 - 夸张的 await:
await 达到了 125.00(毫秒),现代 SSD 正常情况下应在 1-5ms 内,超过 100ms 说明 I/O 队列严重堆积,上游业务肯定已经超时。
此时再用 iotop -o 抓取疯狂写磁盘的内鬼进程,元凶一目了然。
4. 网络疑难杂症:从玄学到科学
- 看连接状态:
ss -s,发现几千个 TIME_WAIT 说明短连接耗尽了端口;大量 CLOSE_WAIT 绝对是业务代码里忘了关闭 Socket 连接。 - 查网卡带宽:
sar -n DEV 1,对比 rxkB/s 和 txkB/s 看是否跑满了物理带宽上限。 - 抓包必杀: 当宏观指标正常但网络不通,直接上
tcpdump 导出 pcap 文件,扔进 Wireshark 分析三次握手和重传包,数据包不会撒谎。
三、K8S 排查特供
传统的 Linux 经验在 Kubernetes 和 Docker 面前,有时会失效,因为你加了一层 Cgroups 和 Namespace。
Node 负载很低,但 Pod 巨卡无比
你用 top 看宿主机,CPU 闲得发慌,但容器里的业务就是说延迟高。
真相:CPU 限流,当应用瞬间突发需要大量计算时,即使宿主机有大量空闲,Cgroups 也会无情地把你的进程暂停。
怎么查: 不要再看 top 了,直接去查宿主机上对应的 Cgroups 状态文件:
cat /sys/fs/cgroup/cpu/cpu.stat
nr_periods 54321
nr_throttled 12500
throttled_time 450000000000
nr_throttled: 进程被限流的周期数,如果这个值占总周期的比例很高(这里 12500 / 54321 约 20% 时间被挂起),说明限流极其严重。
破局之法: 在 K8S 中调大 resources.limits.cpu,或检查应用(如 Java GC 线程数、Go GOMAXPROCS)是否错误识别了宿主机的物理核数导致并发过高。
四、eBPF 显微镜
当你把上述所有工具都用了一遍,依然查不出偶发性的玄学卡顿,你需要祭出核武器:eBPF,通过 bcc-tools,在不修改代码、不重启服务的情况下,洞察内核最深处的动作。
1. execsnoop:
有些疯狂报错的脚本几毫秒就结束了,top 根本抓不到,execsnoop 通过监听 execve 系统调用,让所有新建进程无处遁形。
# 实时查看全系统所有新创建的进程,并显示参数
execsnoop -T
TIME(s) PCOMM PID PPID RET ARGS
03:25:01 cron 4521 1024 0 /usr/sbin/cron -f
03:25:01 sh 4522 4521 0 /bin/sh -c /app/monitor.sh
03:25:02 curl 4523 4522 0 /usr/bin/curl -s http://internal.api/health
解析: PID 与 PPID 父子关系一目了然,清晰展示了 cron 拉起了 monitor.sh 脚本,正在疯狂 curl。
2. opensnoop:
配置怎么改都不生效?应用启动报错但没写缺哪个文件?opensnoop 直接监控全系统文件打开行为。
# 实时监控文件打开情况,并显示错误信息
opensnoop -x
PID COMM FD ERR PATH
10086 java 12 0 /app/logs/error.log
20485 nginx -1 2 /etc/nginx/conf.d/proxy.conf
解析: 注意 ERR 为 2,代表 ENOENT(文件不存在),这就秒杀了“Nginx 莫名报错”的问题,直接锁定了它试图加载不存在的 proxy.conf。
3. biolatency:
iostat 的“平均延迟”往往会掩盖长尾问题(99% 的 I/O 极快,1% 的 I/O 耗时数秒),biolatency 能够以直方图形式,展现真实分布。
# 追踪块设备 I/O 延迟分布(微秒级)
biolatency -m
usecs : count distribution
16 -> 31 : 128 |********** |
32 -> 63 : 512 |****************************************|
64 -> 127 : 231 |****************** |
...
2097152 -> 4194303 : 0 | |
4194304 -> 8388607 : 3 |* |
解析: 绝大多数 I/O 集中在 32 -> 63 微秒,性能极好,但看最后一行,有 3 个请求耗时落在4秒到8秒之间,这就是导致接口随机超时的罪魁祸首,通常预示着底层存储硬件瓶颈或宿主机 I/O 争抢严重。
五、 写在最后:
性能排查,从来不是靠死记硬背几个命令参数就能搞定的,它考察的是工程师对计算机体系结构的深刻理解。
下一次,当报警再次响起,希望你能从容地按照 USE 模型层层递进,从系统负载到 Cgroups 限制,从常规指标到 eBPF 追踪,用数据说话,用逻辑破局。
毕竟,SRE 的价值,不在于重启服务器的速度有多快,而在于能在混沌的故障中,精准地找到那根牵一发而动全身的线头。