跳到正文

Linux 生产排查实战 ​

一句话:生产排查先把问题收敛到 CPU、内存、磁盘、IO、网络中的一类,再用证据链定位到进程或文件;代价是每条命令都要看对指标,否则只是在刷屏。

适用:主流 Linux 发行版(procfs、systemd、sysstat 可用)。jstack、jcmd、jmap、MAT 只适用于 JVM 进程;Go、Python、C/C++ 用各自的 profiler,或 perf/strace/gdb。

为什么需要 ​

线上问题的第一手信息往往只有「变慢了」「打不开了」「磁盘告警」。直接猜代码,容易在错误的方向上浪费几十分钟;只看一个瞬时指标,又可能把噪声当根因。排查流程的价值在于:先按资源分类缩小范围,再按「现象 → 指标 → 证据 → 结论」排除,最后才动代码或配置。

核心机制 ​

分类之后,每一类资源都有固定的证据入口:

类别第一证据常见误判
CPUtop、pidstat、perf负载高不等于 CPU 忙;D 状态进程也会推高 load
内存free -h 的 available、smaps_rollup只看 free 不看 available;把页缓存当泄漏
磁盘df -h、df -i、du只查容量不查 inode;忽略已删除仍被占用的文件
IOiostat -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 飙高 ​

  1. top 找最高进程,top -Hp <pid> 找最高线程。
  2. JVM:printf "%x" 转十六进制后 jcmd <pid> Thread.print 找 nid;非 JVM:perf record、gdb 栈或 strace -c。
  3. 常见根因:死循环/忙等、正则灾难性回溯(预编译不能解决回溯)、大循环字符串拼接、Full GC、%sy 高(系统调用/上下文切换/软中断)。

锁竞争通常表现为「CPU 不高但延迟高」,属于 off-CPU 问题,top 看不出来,要用 perf sched、go tool trace 或应用的锁指标。

内存泄漏 ​

  1. 先分清是进程堆、线程/协程栈、mmap,还是内核/共享内存。
  2. free -h 看 available 趋势,smaps_rollup 看 Rss/Pss 是否持续上涨。
  3. 按语言选工具:JVM 用 GC.class_histogram / GC.heap_dump + MAT Dominator Tree;Go 用 pprof inuse_space;C/C++ 用 heaptrack,valgrind 仅限测试环境。
  4. 常见泄漏源:ThreadLocal 未清理、静态 Map 无界增长、连接池泄漏、Listener 未注销。

磁盘满了 ​

  1. df -h 找分区,df -i 排除 inode 耗尽。
  2. du -xsh /* 逐层下钻。
  3. lsof +L1 找「已删除但进程仍持有」的文件;确认后用 : > /path 截断,不要 rm 后干等。
  4. journalctl --disk-usage 看 systemd 日志,--vacuum-size=500M 收缩前先确认保留策略。
  5. 删除前用 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。最后一条很重要:没有证据链不要重启,重启会把现场清掉。容器环境还要先确认看到的是容器口径还是宿主机口径。

把一门技术讲透到既懂也会用