找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3363

积分

0

好友

449

主题
发表于 昨天 20:36 | 查看: 4| 回复: 0

前言

凌晨两点,手机突然震动。

监控报警:服务器响应延迟从 50ms 飙到 3 秒,CPU 告警。

你打开终端,面对一台“生病”的机器,脑子一片空白,不知道从哪里下手。

这种场景,几乎每个后端工程师都经历过。

这篇文章给你一套可以照着操作的排查流程,从收到报警到定位根因,覆盖 CPU、内存、磁盘 IO、网络四个维度,每步用什么命令、看什么指标,说清楚。

一、先建立全局视角:top / htop

收到报警第一件事,不是立刻猜哪里出问题,而是先看整体。

top

top 的输出很密,先只看这几个关键位置:

系统性能监控指标分析图,含top命令输出及关键指标解读

看完 top 的全局数据,接下来根据哪个维度异常进入对应的专项排查。四个方向,各自有一套命令链路。

二、CPU 异常排查

现象:CPU 使用率居高不下,或 load average 远超核数

第一步:找出耗 CPU 最多的进程

# top 里按 P 键(按 CPU 排序,默认就是)
top

# 或者 ps,精确到进程
ps aux --sort=-%cpu | head -10

第二步:确定是用户态还是内核态

top%Cpu 那行:

  • us(user)高:业务逻辑 CPU 密集,或者有死循环
  • sy(system)高:大量系统调用,可能是频繁 fork()、过多 IO 操作
  • wa(iowait)高:CPU 在等磁盘,瓶颈在 IO 不在 CPU,跳到第四节排查

第三步:定位到具体线程

# 找出进程 4521 下哪个线程最耗 CPU
top -H -p 4521

# 把线程 TID 转成十六进制,对应 gdb/jstack 里的线程 ID
printf '%x\n' <TID>

第四步:用 strace 看它在干什么

strace -p 4521 -c  # 统计模式,看哪个系统调用调用最频繁

上篇 strace 文章讲的用法,这里直接上场。

常见 CPU 高的根因:

  • 死循环(某个 while 条件永远为真)
  • 正则表达式灾难性回溯
  • 频繁 GC(Java 程序 Old Gen 满了)
  • 大量并发请求导致上下文切换过多

三、内存异常排查

现象:内存使用率持续上涨,或 OOM Killer 频繁杀进程

第一步:看内存整体情况

free -h
# 重点看 available(真正可用),不是 free
# Swap 有大量使用说明物理内存已经不够了

cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable|Cached|SwapUsed"

第二步:找内存占用最多的进程

ps aux --sort=-%mem | head -10

# 查看某进程的详细内存分布
cat /proc/<pid>/status | grep -E "VmRSS|VmSwap|VmSize"

第三步:判断是泄漏还是正常增长

# 持续观察进程内存,每2秒刷新一次
watch -n 2 'cat /proc/<pid>/status | grep VmRSS'

# 如果 VmRSS 一直涨,从不下降,基本就是内存泄漏

第四步:检查 OOM 记录

dmesg | grep -i "oom\|killed process" | tail -20
# 如果有 OOM 记录,说明已经有进程被内核杀过了

有了之前 OOM Killer 那篇的基础,看到这段日志就知道哪个进程的 oom_score 被打高了。

常见内存问题根因:

  • C/C++ 程序内存泄漏(malloc 了不 free
  • Java 堆内存配置过小,频繁触发 Full GC
  • 内存碎片化严重(用 jemalloc 替换 ptmalloc 可缓解)
  • 单个进程占用内存过大,挤压其他服务

四、磁盘 IO 异常排查

现象:服务响应慢,CPU 的 wa 指标偏高,磁盘读写告警

第一步:确认 IO 瓶颈

iostat -x 1 5
# -x 显示扩展指标,1秒刷新一次,共5次

重点看这几列:

磁盘性能指标分析表,含util/await/r_s_w_s等关键指标解读

第二步:找出哪个进程在猛读写磁盘

# 实时显示各进程的磁盘 IO 占用
iotop -o   # -o 只显示有 IO 活动的进程

第三步:如果是日志/数据库写入导致的,检查 Page Cache

回忆上一篇 Page Cache 文章:write() 默认先写 Page Cache,内核异步刷盘。如果程序有大量 fsync(),每次都要等磁盘,await 就会飙高。

# 查看脏页数量,如果很大说明积压了大量待刷数据
cat /proc/meminfo | grep -i dirty

常见 IO 问题根因:

  • 数据库没有走索引,全表扫描产生大量随机 IO
  • 日志级别太低(DEBUG 级别),每条请求都写日志
  • 没有正确配置 fsync 策略,每次写入都等磁盘
  • SSD 写入放大,大量小块随机写(fio 可以测试)

五、网络异常排查

现象:接口超时、连接数打满、TIME_WAIT 堆积

第一步:看整体网络连接状态

# 统计各状态的 TCP 连接数
ss -s

# 详细看当前所有连接
ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn

输出大概长这样:

2341 TIME_WAIT    ← 太多说明短连接频繁,之前 TIME_WAIT 篇讲过
 512 ESTABLISHED  ← 正常连接数
  48 CLOSE_WAIT   ← 多了说明有连接没有正常关闭,代码有 bug
   3 LISTEN

CLOSE_WAIT 是一个特别值得关注的状态——它意味着对端已经关闭连接(发了 FIN),但本端还没有调用 close()。如果 CLOSE_WAIT 持续积累,通常是代码里没有正确关闭连接。

第二步:看端口连接和带宽

# 查看哪个端口连接最多
ss -tan | grep ESTABLISHED | awk '{print $5}' | cut -d: -f2 | sort | uniq -c | sort -rn

# 实时查看网络带宽使用
iftop -n        # 按连接维度
nethogs         # 按进程维度(哪个进程在吃带宽)

第三步:排查丢包和延迟

# 基本连通性和延迟
ping -c 10 <目标IP>

# 看路由路径,找延迟在哪一跳
traceroute <目标IP>

# 抓包,真正看数据在传什么
tcpdump -i eth0 -w /tmp/capture.pcap port 8080
# 然后用 Wireshark 打开分析

第四步:检查内核网络参数

# 查看连接队列是否有溢出(SYN flood 或并发过高时会满)
ss -lnt   # 看 Send-Q 列,如果持续非0说明队列满了
netstat -s | grep -i "listen\|overflow\|drop"

常见网络问题根因:

  • TIME_WAIT 过多导致端口耗尽(SO_REUSEADDR 和连接池可解决)
  • CLOSE_WAIT 积累(代码没有正确关闭连接)
  • 带宽打满(大文件传输、日志采集等占满上行带宽)
  • DNS 解析慢(每次请求都 DNS 查询且没有缓存)

六、四维排查的完整决策图

把上面的流程整理成一张决策图,收到报警的时候对着查:

系统性能故障排查流程图,含CPU高、内存高、磁盘IO高、网络异常四分支排查路径

七、一个完整的真实排查案例

把上面的流程串一遍,看一个真实场景:

现象:下午 3 点接到报警,API 响应时间从 80ms 涨到 2 秒,CPU 告警。

第一步:top 看全局

load average: 12.3, 8.1, 5.2   ← 明显超过 CPU 核数(8核)
%Cpu: 42 us, 8 sy, 0 ni, 2 id, 45 wa   ← wa=45%!IO 等待极高
MiB Mem: 15832 total, 892 free

wa 高 → 不是 CPU 问题,是 IO 问题。CPU 在等磁盘。

第二步:iostat 确认 IO 瓶颈

iostat -x 1
# sda  r/s=2   w/s=4800   await=120ms   %util=98%

%util=98%,磁盘已经跑满。每秒 4800 次写入,await 高达 120ms。

第三步:iotop 找到是谁在写

PID   DISK READ  DISK WRITE  COMMAND
4521  0.00 B/s   480 M/s     java

Java 进程在疯狂写磁盘,每秒 480MB。

第四步:strace 看写的是什么

strace -p 4521 -e write 2>&1 | head -20
# write(3, "DEBUG 2024-03-15 ...", 256) = 256
# write(3, "DEBUG 2024-03-15 ...", 256) = 256
# ...无限循环...

全是 DEBUG 级别日志在写文件 fd=3。

根因:有人在生产环境打开了 DEBUG 日志,每个请求写十几条 DEBUG 日志,高峰期请求量一上来,直接把磁盘 IO 打满。

修复:关掉 DEBUG 日志,CPU wa 立刻从 45% 降到 2%,响应时间回到 80ms。

八、最常用命令速查表

收藏起来,下次遇到报警直接翻:

# ===== 全局 =====
top                         # 实时全貌,按 M 内存排序,按 P CPU排序
vmstat 1                    # 每秒刷新,看 CPU/内存/IO 综合指标
uptime                      # 快速看 load average

# ===== CPU =====
ps aux --sort=-%cpu | head  # CPU 占用 TOP 进程
top -H -p <pid>             # 看进程内线程 CPU
perf top                    # 函数级 CPU 热点(高级)

# ===== 内存 =====
free -h                     # 内存概况
ps aux --sort=-%mem | head  # 内存占用 TOP 进程
cat /proc/<pid>/status      # 某进程内存详情
dmesg | grep oom            # OOM 历史记录

# ===== IO =====
iostat -x 1                 # 磁盘 IO 详情
iotop -o                    # 哪个进程在读写
lsof +D /path               # 谁打开了某目录下的文件

# ===== 网络 =====
ss -s                       # TCP 连接状态统计
ss -tan | grep TIME_WAIT | wc -l  # TIME_WAIT 数量
iftop -n                    # 实时网络流量(按连接)
nethogs                     # 实时网络流量(按进程)
tcpdump -i eth0 port 8080   # 抓包

# ===== 通用深挖 =====
strace -p <pid>             # 看进程在做什么系统调用
lsof -p <pid>               # 看进程打开了哪些文件/连接

写在最后

服务器排查从来不是运气,是一套系统化的思维方式:

先看全局(top)→ 判断哪个维度异常 → 找到异常的进程 → 用 strace / lsof 看它在做什么 → 结合内核原理理解根因。

这篇文章涉及的所有工具和原理,在之前的系列里都讲过:strace 怎么用、Page Cache 为什么影响 IO 性能、OOM Killer 为什么杀了意料之外的进程、TIME_WAIT 为什么会堆积……一篇一篇积累下来,排查的时候就能快速对号入座。

知识要用起来才算真正掌握。如果你也希望在真实的项目中打磨这些技术直觉,而不是停留在理论层面,不妨在云栈社区找找灵感,和更多同行交流实战踩坑经验。




上一篇:TCP三次握手四次挥手图解:6张图带你吃透连接机制
下一篇:GMSSH桌面级AI运维终端:Docker可视化管理,告别Linux命令行
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-5-8 00:26 , Processed in 0.623689 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表