☰
Linux线上问题排查实战:系统资源、进程线程、网络连接与日志追踪命令详解
2026/10/14 7:27:53 网站建设 项目流程

1. 线上问题排查的核心思路与命令分类

线上出问题的时候,最怕的不是问题本身有多复杂,而是手忙脚乱不知道从哪里下手。我见过不少同行,一听到告警就立刻重启服务,结果现场没了,问题原因也查不到,下次照样复现。排查这件事,核心原则就一条:先保留现场,再定位根因,最后才动手恢复。命令只是工具,脑子里得先有一张排查地图。

1.1 为什么命令排查依然是第一手段

现在可观测性体系很成熟了,链路追踪、指标监控、日志聚合平台一大堆。但真到了紧急时刻,图形界面加载慢、查询有延迟、采样数据缺失,反而是直接登录机器敲命令来得最快最准。命令行的优势在于:实时性高、不受采集链路影响、能拿到最原始的数据。监控平台看到的是“过去一分钟的平均值”,而命令看到的是“此刻这一秒的真实状态”,这两者在排查抖动类问题时差别巨大。

另一个现实原因是,很多问题发生在监控覆盖不到的角落。比如某个进程的文件描述符悄悄泄漏、某块磁盘的inode被耗尽、某个连接处于CLOSE_WAIT状态堆积——这些细节往往要落到具体命令上才能看清。所以我的习惯是,监控负责“发现异常”,命令负责“确认和深挖”。

1.2 排查命令的四大分类框架

我把线上排查常用的命令按用途分成四类,这样遇到问题时能快速定位该用哪一类:

分类核心目标代表命令
系统资源类看CPU、内存、磁盘、负载top、free、df、iostat、vmstat
进程线程类看进程状态、线程栈、句柄ps、pidstat、jstack、pstree
网络连接类看连接状态、端口、流量ss、netstat、tcpdump、lsof
日志追踪类看实时日志、关键字过滤tail、grep、journalctl、awk

这个分类不是死的,实际排查中经常交叉使用。比如发现CPU飙高,先用top定位到进程,再用pidstat看是哪个线程,最后用jstack抓线程栈分析代码。整个过程就是在这四类之间来回跳。

提示:排查前先确认自己有没有权限。很多生产环境的命令需要特定用户或sudo权限,临时申请权限会耽误时间,平时就要把常用命令的权限准备好。

1.3 排查的黄金顺序:从宏观到微观

新手最容易犯的错是一上来就盯着某个细节猛查,结果方向错了白费功夫。我的经验是严格遵循“从宏观到微观”的顺序:

  1. 先看整体负载:uptime、top,判断是全局问题还是局部问题
  2. 再看资源瓶颈:CPU、内存、磁盘、网络,确定是哪类资源吃紧
  3. 然后定位到进程:找到消耗资源的罪魁祸首
  4. 最后深入进程内部:线程栈、句柄、连接,找到具体代码或配置问题

这个顺序能保证你不漏掉关键信息,也不会在错误的方向上浪费时间。下面几个章节,我就按这个思路把常用命令一个个拆开讲。

2. 系统资源类命令的实战用法

系统资源是排查的起点。很多时候服务变慢,不是代码问题,而是机器本身扛不住了。这一章把CPU、内存、磁盘、负载相关的命令讲透。

2.1 top命令:不只是看CPU

top大概是所有人第一个学会的排查命令,但真正用明白的人不多。默认输出里,第一行是负载,第二行是进程统计,第三行是CPU,第四行是内存,第五行是交换分区。很多人只看CPU那一行的us(用户态)和sy(内核态),其实信息量远不止这些。

几个容易被忽略的关键指标:

  • load average:三个数字分别是1分钟、5分钟、15分钟的平均负载。判断标准是跟CPU核数比,比如8核机器负载持续超过8就要警惕。但要注意,负载高不一定是CPU忙,也可能是大量进程在等IO。
  • wa(iowait):CPU等待IO的时间占比。这个值高说明磁盘是瓶颈,而不是CPU。
  • si/so:交换分区的换入换出。这两个值非零且持续,说明物理内存不够了,系统在拿磁盘当内存用,性能会断崖式下跌。
  • %Cpu(s)的st:被虚拟机偷走的时间。如果是云主机,这个值高说明宿主机超卖了。

top的交互操作也很实用:按P按CPU排序,按M按内存排序,按1展开每个核的详情,按H显示线程。排查时我习惯先按1看是不是单核打满,再按P找最耗CPU的进程。

注意:top默认3秒刷新一次,对于瞬时抖动可能抓不到。可以改成top -d 1加快刷新,或者用top -b -n 1输出一次快照方便记录。

2.2 free命令:内存的真实账本

free的输出里,重点看available这一列,而不是free那一列。因为Linux会把空闲内存拿去做缓存(buff/cache),free看起来很少是正常的,available才是真正可用的内存。

free -h total used free shared buff/cache available Mem: 15Gi 8.2Gi 1.1Gi 320Mi 6.1Gi 6.8Gi Swap: 2.0Gi 512Mi 1.5Gi

判断内存是否真的紧张,看两个信号:一是available持续低于总内存的10%,二是swap的used在持续增长。如果swap用得很多,说明系统已经在频繁换页,这时候任何服务都会变慢。

我踩过的一个坑:有次服务频繁GC,查了半天代码,最后发现是机器内存被另一个进程吃光了,导致JVM的堆被换到swap里。所以内存排查一定要看全局,不能只盯着自己的服务。

2.3 df和du:磁盘空间与inode

磁盘满了是线上最常见的故障之一。df看分区使用率,du看目录占用。但有个细节很多人不知道:df看的是块使用率,inode是另一套账。

df -h # 看块使用率 df -i # 看inode使用率 du -sh /var/log/* | sort -rh | head -20 # 找大目录

inode耗尽的表现很迷惑:df -h显示还有空间,但就是写不进文件,报“No space left on device”。这时候用df -i一看,inode用满了。inode被什么占了呢?通常是大量小文件,比如日志切分产生的海量小文件、临时文件没清理、邮件队列堆积等。

找inode大户可以用:

find / -xdev -printf '%h\n' | sort | uniq -c | sort -rn | head -20

这条命令统计每个目录下的文件数量,排在前面的就是小文件重灾区。

提示:删除大量小文件时,直接用rm -rf可能很慢甚至卡住。可以用find 目录 -type f -delete,或者先rsync一个空目录过去覆盖,速度更快。

2.4 iostat和vmstat:IO瓶颈的照妖镜

CPU不高、内存也够,但服务就是慢,这时候要怀疑IO。iostat看磁盘,vmstat看整体。

iostat -x 1 5 # 每1秒采样,共5次,-x显示扩展信息

重点看几个列:%util是磁盘繁忙度,接近100%说明磁盘跑满了;await是平均IO等待时间,单位毫秒,机械盘超过20ms、SSD超过5ms就要注意;r/s和w/s是每秒读写次数。

vmstat的输出里,r列是等待运行的进程数,持续大于CPU核数说明CPU不够;b列是阻塞的进程数,高说明IO有问题;si和so跟free里的swap对应。

我一般把iostat和vmstat配合用:vmstat发现b列高,再用iostat确认是哪块盘的问题。这样定位效率最高。

3. 进程与线程类命令的深挖技巧

定位到具体进程之后,工作才真正开始。这一章讲怎么把进程和线程看透。

3.1 ps命令的几种实用组合

ps的参数组合非常多,我常用的就几个:

ps -ef | grep 服务名 # 看进程是否存在 ps -aux --sort=-%cpu | head -10 # 按CPU排序看前10 ps -aux --sort=-%mem | head -10 # 按内存排序看前10 ps -eLf | grep 进程名 # 看进程下的所有线程

ps -eLf这个组合特别有用,它能列出每个线程的LWP(轻量级进程ID),配合后面要讲的jstack或gdb就能定位到具体线程。

还有一个技巧是看进程的启动时间,判断是不是被意外重启过:

ps -eo pid,lstart,etime,cmd | grep 服务名

lstart是启动时间,etime是运行时长。如果发现运行时长只有几分钟,而服务本该跑了好几天,那说明进程刚重启过,问题可能跟重启有关。

3.2 pidstat:进程级的细粒度监控

top只能看进程的整体情况,pidstat能拆到每个进程甚至每个线程。

pidstat -p 12345 1 5 # 监控指定进程,每秒一次共5次 pidstat -t -p 12345 1 5 # -t 显示线程级数据 pidstat -d -p 12345 1 5 # -d 显示IO数据

排查CPU问题时,pidstat -t能直接告诉你哪个线程在烧CPU,拿到线程ID(TID)后就能去抓线程栈。这个链路是:top找进程 → pidstat -t找线程 → jstack抓栈 → 定位代码。

3.3 jstack抓线程栈的正确姿势

Java服务排查绕不开jstack。但直接jstack pid抓出来的栈可能不准,因为抓的瞬间线程状态可能已经变了。我的做法是连续抓三次,间隔几秒,对比看哪些线程一直处于RUNNABLE或BLOCKED。

jstack 12345 > stack1.txt sleep 3 jstack 12345 > stack2.txt sleep 3 jstack 12345 > stack3.txt

然后重点看nid(native thread id)跟前面pidstat拿到的TID对得上的线程。注意jstack里的nid是十六进制,pidstat的TID是十进制,要转换一下:

printf '%x\n' 12345 # 十进制转十六进制

死锁排查也是jstack的强项,输出里搜deadlock关键字,JVM会直接告诉你哪几个线程互相等锁。

注意:jstack会暂停JVM一小会儿(安全点),对高并发服务有轻微影响。生产环境抓栈要避开业务高峰,或者用-F强制抓取(但可能不准)。

3.4 lsof:一切皆文件的哲学

Linux里一切皆文件,lsof就是查看“谁打开了什么文件”的利器。排查句柄泄漏、端口占用、文件被删但空间没释放,都靠它。

lsof -p 12345 # 看进程打开的所有文件 lsof -i :8080 # 看谁占用了8080端口 lsof | grep deleted # 看已删除但未释放的文件 lsof -p 12345 | wc -l # 统计进程打开的文件数

“文件已删除但空间没释放”是经典问题:日志文件被rm了,但进程还持有句柄,磁盘空间不回收。用lsof | grep deleted找到后,重启进程或清空句柄才能释放空间。

句柄泄漏的判断:lsof -p pid | wc -l持续增长,接近ulimit -n的限制时,服务就会报“Too many open files”。这时候要结合代码看哪里没关流。

4. 网络连接类命令的排查套路

网络问题最隐蔽,因为涉及两端甚至多端。这一章讲怎么把连接状态看清楚。

4.1 ss替代netstat的理由

netstat是老牌命令,但在连接数多的时候性能很差,因为它要遍历/proc下的所有文件。ss直接从内核拿数据,快得多。现在新系统基本都用ss了。

ss -s # 汇总统计 ss -tunlp # 看TCP/UDP监听端口 ss -tan state established | wc -l # 统计已建立连接数 ss -tan state time-wait | wc -l # 统计TIME_WAIT数

连接状态里,几个需要重点关注的:

  • TIME_WAIT:主动关闭方会进入这个状态,持续2MSL(通常60秒)。大量TIME_WAIT会占用端口资源,可以通过调整内核参数优化。
  • CLOSE_WAIT:被动关闭方如果没正确关闭连接,会一直停在这个状态。CLOSE_WAIT堆积基本等于代码有bug,是应用没调close()导致的。
  • SYN_RECV:半连接状态,大量出现可能是SYN Flood攻击,或者后端处理太慢。

我排查连接问题的习惯是先ss -s看全局,再按状态过滤看具体是哪种连接异常。

4.2 tcpdump抓包的最小必要原则

tcpdump功能强大,但在生产环境抓包要极其克制,因为抓包本身消耗CPU和磁盘,抓多了可能把机器拖垮。我的原则是:明确目标、限制数量、及时停止。

tcpdump -i eth0 -nn -c 100 port 8080 -w /tmp/cap.pcap

参数解释:-i指定网卡,-nn不解析域名和端口名(更快),-c 100只抓100个包就停,-w写入文件而不是打印到屏幕。

抓包前先想清楚要抓什么:是抓某个端口的握手过程,还是抓某个IP的流量,还是抓特定协议的包。目标越明确,抓包量越小,对生产影响越小。

分析抓包文件可以用tcpdump -r读回来,或者下载到本地用Wireshark看。重点看三次握手是否完成、有没有RST包、重传多不多。

注意:抓包文件可能包含敏感数据,分析完要及时删除,不要留在生产机器上。

4.3 网络连通性排查的递进顺序

网络不通的时候,按这个顺序排查最省时间:

  1. 本机网络:ip addr看网卡是否up,ip route看路由表
  2. 网关连通:ping 网关IP,不通说明本机网络配置有问题
  3. 目标连通:ping 目标IP,不通可能是路由或防火墙
  4. 端口连通:telnet 目标IP 端口或nc -zv 目标IP 端口,确认端口是否开放
  5. DNS解析:nslookup 域名或dig 域名,确认域名解析正常
  6. 抓包确认:前面都通但业务还异常,就抓包看实际流量

这个顺序能快速缩小问题范围,避免盲目猜测。

5. 日志追踪类命令的高效用法

日志是排查的最后一公里,也是最直接的信息来源。但日志量大,怎么快速找到有用的那几行是关键。

5.1 tail和grep的黄金组合

实时看日志用tail -f,配合grep过滤关键字:

tail -f app.log | grep --line-buffered "ERROR"

--line-buffered很重要,不加的话grep会缓冲输出,看起来像卡住了。如果要同时看多个关键字:

tail -f app.log | grep -E "ERROR|WARN|Exception"

查历史日志用grep加时间范围:

grep "2024-01-15 14:3" app.log | grep "ERROR"

5.2 awk和sed的日志分析技巧

日志里经常要统计某些指标,比如统计每个接口的调用次数、统计错误码分布。这时候awk就派上用场了。

# 统计每种错误码出现次数 awk '{print $9}' access.log | sort | uniq -c | sort -rn # 统计每分钟的请求量 awk '{print substr($4, 2, 17)}' access.log | uniq -c # 找出响应时间超过1秒的请求 awk '$NF > 1 {print}' access.log

sed适合做日志的提取和替换,比如提取某个时间段的日志:

sed -n '/14:30:00/,/14:35:00/p' app.log

这些命令组合起来,能在几秒内从GB级日志里捞出关键信息,比图形界面快得多。

5.3 journalctl:系统日志的统一入口

用systemd的系统,服务日志都进了journal,用journalctl查看:

journalctl -u 服务名 -f # 实时跟踪某服务 journalctl -u 服务名 --since "10 min ago" # 看最近10分钟 journalctl -p err -b # 看本次启动的所有错误 journalctl -u 服务名 -o json-pretty # JSON格式输出,方便解析

journalctl的好处是自带时间过滤和优先级过滤,不用自己写grep。但要注意journal默认会限制日志大小,老日志可能被清理,重要日志还是要单独落盘。

6. 常见问题排查速查与避坑经验

前面讲了命令怎么用,这一章把常见问题跟对应命令串起来,形成速查表,再分享几个我踩过的坑。

6.1 典型故障场景速查表

故障现象首选命令关键指标可能原因
服务响应慢top、iostatload、%util、awaitCPU/IO瓶颈
内存持续增长free、psavailable、RSS内存泄漏
磁盘写满df -h、df -iUse%、IUse%空间或inode耗尽
连接超时ss、tcpdumpCLOSE_WAIT、重传连接泄漏或网络抖动
端口占用lsof -i、ss监听状态端口冲突
句柄耗尽lsof、ulimit打开文件数流未关闭
CPU飙高top、pidstat -t%CPU、TID死循环或频繁GC
线程卡死jstackBLOCKED、deadlock死锁或锁竞争

这张表建议打印出来贴在工位上,出问题时对照着查,能省不少时间。

6.2 我踩过的三个坑

第一个坑:重启掩盖了真相。早期遇到服务异常,第一反应是重启,结果重启后问题暂时消失,过几天又出现,反复几次都没找到根因。后来养成习惯:重启前先抓一份现场,包括线程栈、堆转储、连接状态。哪怕最后还是要重启,至少留下了分析材料。

第二个坑:只看平均值。监控平台给的是分钟级平均值,有次服务每隔几分钟抖动一下,平均值看起来完全正常。后来用top -d 1和pidstat 1看秒级数据,才发现是定时任务在特定时刻抢资源。排查抖动类问题,一定要看细粒度数据。

第三个坑:忽略系统日志。有次应用日志一切正常,但服务就是起不来,查了半天代码。最后dmesg一看,是OOM Killer把进程杀了。系统日志(dmesg、journalctl)经常被忽略,但它记录了内核层面的关键事件,排查疑难问题必看。

6.3 排查效率提升的几个习惯

  • 提前准备好命令清单:把常用命令写成脚本或别名,出问题时直接调用,不用现查参数。
  • 保留排查记录:每次排查的过程和结论记下来,形成自己的知识库,下次遇到类似问题能快速复用。
  • 定期演练:在测试环境模拟各种故障,练习排查流程,真出问题时才不会慌。
  • 关注基线:平时记录服务的正常指标范围,异常时一对比就知道偏了多少。

排查这件事,命令是死的,思路是活的。同样一套命令,有经验的人能在几分钟内定位问题,没经验的人可能查半天还在外围打转。差别就在于脑子里有没有那张排查地图,以及平时有没有积累足够的实战经验。多练、多记、多复盘,慢慢就能形成自己的排查直觉。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询