主题
Linux 生产排查实战
一句话:生产排查先把问题收敛到 CPU、内存、磁盘、IO、网络中的一类,再用证据链定位到进程或文件;代价是每条命令都要看对指标,否则只是在刷屏。
适用:主流 Linux 发行版(procfs、systemd、sysstat 可用)。
jstack、jcmd、jmap、MAT 只适用于 JVM 进程;Go、Python、C/C++ 用各自的 profiler,或perf/strace/gdb。
为什么需要
线上问题的第一手信息往往只有「变慢了」「打不开了」「磁盘告警」。直接猜代码,容易在错误的方向上浪费几十分钟;只看一个瞬时指标,又可能把噪声当根因。排查流程的价值在于:先按资源分类缩小范围,再按「现象 → 指标 → 证据 → 结论」排除,最后才动代码或配置。
核心机制
分类之后,每一类资源都有固定的证据入口:
| 类别 | 第一证据 | 常见误判 |
|---|---|---|
| CPU | top、pidstat、perf | 负载高不等于 CPU 忙;D 状态进程也会推高 load |
| 内存 | free -h 的 available、smaps_rollup | 只看 free 不看 available;把页缓存当泄漏 |
| 磁盘 | df -h、df -i、du | 只查容量不查 inode;忽略已删除仍被占用的文件 |
| IO | iostat -x 的 await、%util | 把 %util 当饱和度;svctm 已在 sysstat 12 移除 |
| 网络 | ss、netstat -s、sar -n DEV | 只看连接数,不看重传、TIME_WAIT、队列 |
看趋势比看瞬时值重要:至少观察 1–5 分钟,或者用 sar/pidstat 回看。定位到进程后,再按语言选栈工具。
最小上手
bash
# CPU
top -Hp <pid>
ps -L -o pid,tid,pcpu,stat,comm -p <pid> | sort -k3 -nr | head
printf "%x\n" <tid> # TID 转十六进制,匹配 jstack 的 nid
jcmd <pid> Thread.print # JVM
perf top -p <pid> # 非 JVM 采样
perf record -F 99 -g -p <pid> -- sleep 30 && perf report
# 内存
free -h
cat /proc/<pid>/smaps_rollup # 内核 4.14+
pmap -x <pid> | sort -k3 -nr | head -20
dmesg -T | grep -iE "out of memory|killed process|oom-kill"
jcmd <pid> GC.class_histogram | head -30 # JVM,可能触发 Full GC
# 磁盘
df -h; df -i
du -xsh /* 2>/dev/null | sort -rh | head -10
lsof +L1 # 已删除但仍被进程持有
journalctl --disk-usage
# IO
iostat -x 1 10
iotop -o
pidstat -d 1
# 网络
ss -s
ss -ant state established | wc -l
ss -ant state time-wait | wc -l
ss -lntp
netstat -s | grep -i retrans
sar -n DEV 1
# 进程与日志
cat /proc/<pid>/status
cat /proc/<pid>/limits # 比 ulimit -n 更准
ls /proc/<pid>/fd | wc -l
uptime # 含 D 状态,不等于 CPU 使用率
vmstat 1
dmesg -T | tail -50
journalctl -u <svc> --since "10 min ago" -f
tail -f app.log
grep -nE "ERROR|FATAL" app.log | tail -50生产动作
CPU 飙高
top找最高进程,top -Hp <pid>找最高线程。- JVM:
printf "%x"转十六进制后jcmd <pid> Thread.print找nid;非 JVM:perf record、gdb栈或strace -c。 - 常见根因:死循环/忙等、正则灾难性回溯(预编译不能解决回溯)、大循环字符串拼接、Full GC、
%sy高(系统调用/上下文切换/软中断)。
锁竞争通常表现为「CPU 不高但延迟高」,属于 off-CPU 问题,top 看不出来,要用 perf sched、go tool trace 或应用的锁指标。
内存泄漏
- 先分清是进程堆、线程/协程栈、mmap,还是内核/共享内存。
free -h看 available 趋势,smaps_rollup看 Rss/Pss 是否持续上涨。- 按语言选工具:JVM 用
GC.class_histogram/GC.heap_dump+ MAT Dominator Tree;Go 用 pprofinuse_space;C/C++ 用 heaptrack,valgrind 仅限测试环境。 - 常见泄漏源:ThreadLocal 未清理、静态 Map 无界增长、连接池泄漏、Listener 未注销。
磁盘满了
df -h找分区,df -i排除 inode 耗尽。du -xsh /*逐层下钻。lsof +L1找「已删除但进程仍持有」的文件;确认后用: > /path截断,不要rm后干等。journalctl --disk-usage看 systemd 日志,--vacuum-size=500M收缩前先确认保留策略。- 删除前用
lsof /path确认没有进程还在写。
边界
jstack/jcmd/jmap/MAT 只对 JVM 有效;其他语言要用对应 profiler,别把jstack套到 Go 进程上。perf、strace、gdb有权限和性能开销;生产上限定采样时间,strace -f在高并发进程上可能放大延迟。jmap -histo:live和堆转储可能触发 Full GC 并停顿,先确认可接受的停顿窗口。- 容器里看到的
/proc、free、df可能是宿主机口径;先确认 cgroup 限制和挂载命名空间。 dmesg可能受kernel.dmesg_restrict限制;uptime的 load 含不可中断睡眠,不能直接当 CPU 使用率。- 没有证据链时不要重启:重启会清掉现场,先抓
top、栈、日志和dmesg。
讲清楚
Linux 排查的核心不是记住多少命令,而是先分类再定位。看到「变慢」,先判断是 CPU、内存、磁盘、IO 还是网络;每一类都有第一证据,比如 CPU 看
top -Hp,内存看smaps_rollup,磁盘先分容量和 inode,网络先看重传和连接状态。定位到进程后,再按语言选工具:JVM 用jcmd/jstack,Go 用 pprof,C/C++ 用 perf 或 heaptrack。最后一条很重要:没有证据链不要重启,重启会把现场清掉。容器环境还要先确认看到的是容器口径还是宿主机口径。