Linux线上问题排查实战:系统资源、进程线程、网络连接与日志追踪命令详解 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 排查的黄金顺序从宏观到微观新手最容易犯的错是一上来就盯着某个细节猛查结果方向错了白费功夫。我的经验是严格遵循“从宏观到微观”的顺序先看整体负载uptime、top判断是全局问题还是局部问题再看资源瓶颈CPU、内存、磁盘、网络确定是哪类资源吃紧然后定位到进程找到消耗资源的罪魁祸首最后深入进程内部线程栈、句柄、连接找到具体代码或配置问题这个顺序能保证你不漏掉关键信息也不会在错误的方向上浪费时间。下面几个章节我就按这个思路把常用命令一个个拆开讲。2. 系统资源类命令的实战用法系统资源是排查的起点。很多时候服务变慢不是代码问题而是机器本身扛不住了。这一章把CPU、内存、磁盘、负载相关的命令讲透。2.1 top命令不只是看CPUtop大概是所有人第一个学会的排查命令但真正用明白的人不多。默认输出里第一行是负载第二行是进程统计第三行是CPU第四行是内存第五行是交换分区。很多人只看CPU那一行的us用户态和sy内核态其实信息量远不止这些。几个容易被忽略的关键指标load average三个数字分别是1分钟、5分钟、15分钟的平均负载。判断标准是跟CPU核数比比如8核机器负载持续超过8就要警惕。但要注意负载高不一定是CPU忙也可能是大量进程在等IO。waiowaitCPU等待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/cachefree看起来很少是正常的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和vmstatIO瓶颈的照妖镜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拿到线程IDTID后就能去抓线程栈。这个链路是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然后重点看nidnative 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 网络连通性排查的递进顺序网络不通的时候按这个顺序排查最省时间本机网络ip addr看网卡是否upip route看路由表网关连通ping 网关IP不通说明本机网络配置有问题目标连通ping 目标IP不通可能是路由或防火墙端口连通telnet 目标IP 端口或nc -zv 目标IP 端口确认端口是否开放DNS解析nslookup 域名或dig 域名确认域名解析正常抓包确认前面都通但业务还异常就抓包看实际流量这个顺序能快速缩小问题范围避免盲目猜测。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 ERROR5.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.logsed适合做日志的提取和替换比如提取某个时间段的日志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 排查效率提升的几个习惯提前准备好命令清单把常用命令写成脚本或别名出问题时直接调用不用现查参数。保留排查记录每次排查的过程和结论记下来形成自己的知识库下次遇到类似问题能快速复用。定期演练在测试环境模拟各种故障练习排查流程真出问题时才不会慌。关注基线平时记录服务的正常指标范围异常时一对比就知道偏了多少。排查这件事命令是死的思路是活的。同样一套命令有经验的人能在几分钟内定位问题没经验的人可能查半天还在外围打转。差别就在于脑子里有没有那张排查地图以及平时有没有积累足够的实战经验。多练、多记、多复盘慢慢就能形成自己的排查直觉。