Skip to content

服务器莫名卡顿排查 ​

"服务器偶尔卡一下又恢复了"是最难排查的性能问题类——没有崩溃、没有错误日志、监控曲线只有不明显的毛刺。这类问题的排查方法论是:先定位"卡"的类型(CPU/内存/IO/网络/锁),再定位时刻(什么时候卡),最后定位到具体进程或内核路径。顺序反了就会陷入"什么都查了什么都正常"的循环。

第一步:确认卡顿的形态 ​

"卡"这个词掩盖了完全不同的故障模式。先问用户或观察现象把它归类:

交互卡(SSH 登录慢、命令响应慢)→ 通常是 CPU 饱和(load 高)、内存回收(swap 活跃)、或 IO 等待——系统级问题。吞吐卡(请求延迟升高但系统看起来空闲)→ 通常是锁竞争、连接池耗尽、下游依赖慢——应用级问题。周期性卡(每隔 N 分钟/小时卡一次)→ 定时任务(cron、日志轮转、备份)、GC、或监控采集本身——周期性指纹是最有价值的线索。随机卡(无规律)→ 竞态类(锁、连接耗尽)或外部依赖(网络抖动、共享存储邻居噪声)。

第二步:锁定时刻 ​

瞬时的卡顿在平均值监控里不可见——1 分钟采样率的监控会漏掉 5 秒的卡顿。排查手段按时间精度递进:

bash
# uptime/loadavg 的三时间窗——1分钟 vs 15分钟的差值指示"正在发生"还是"已经过去"
uptime

# vmstat 的 r 列(运行队列)和 wa 列(IO等待)——1秒间隔持续观察
vmstat 1

# 卡顿发生时的第一手数据:sysstat 的历史(如果部署了 sadc)
sar -q -f /var/log/sa/sa14    # 当天的队列数据
sar -B                        # 页回收活动
sar -d                        # 每设备 IO

如果卡顿还会发生,vmstat 1 挂在终端上等待下一次卡顿出现是最朴素也最有效的手段——卡顿发生那一秒的 r/wa/si 列的突变直接指向方向。

第三步:按方向深入 ​

CPU 方向 ​

load 高但 CPU 使用率不高 → 不是计算瓶颈而是等待——load 统计的是"运行队列 + 不可中断睡眠"(Linux 的 load 含 D 状态进程),高 load + 低 CPU% 的组合指向 IO 等待或内存问题。

CPU 使用率的细分比总量更有信息量:%iowait 高 → IO 等待(转 IO 方向);%sys 高 → 内核态消耗(syscall 风暴、锁自旋、软中断);%soft 高 → 网络软中断(可能有中断风暴);%steal 高 → 虚机邻居抢占(云上宿主机超卖——你控制不了但至少能确认)。

bash
# 按 CPU 核查看——单核打满 vs 均匀分布是完全不同的问题
mpstat -P ALL 1
# 单核 100% softirq:中断亲和把流量集中到一个核(RSS/RPS 配置问题)

内存方向 ​

内存不足的卡顿指纹是回收活动而非使用率——使用率 90% 但无回收则完全健康(缓存占满),使用率 70% 但疯狂回收则严重卡顿。关键指标:pgscan/pgsteal(直接回收——进程在分配路径上同步等回收,延迟飙升的主因)、si/so(swap 活动)、major fault(需要从磁盘读页)。

bash
vmstat 1        # si/so 非零 = swap 活跃
sar -B          # pgscand(直接回收)非零 = 分配路径上的同步痛苦
cat /proc/pressure/vm   # PSI(Pressure Stall Information)——内核直接告诉你"有多少时间浪费在等内存"

PSI(内核 4.20+)是"莫名卡顿"的利器:some 行是"至少一个任务在等待",full 行是"所有任务都在等"——full 的任何非零读数都是硬证据。CPU 和 IO 也有对应的 PSI 文件。

IO 方向 ​

iowait 高 + 设备 util 高 → 磁盘真实饱和;iowait 高 + 设备 util 不高 → IO 请求的并发排队问题(队列深度、条带不均)或远端存储的延迟(NVSAN、网络存储抖动)。注意 util 的陷阱:SSD/NVMe 的并行能力使 util 100% 不代表饱和(设备可以同时处理很多请求)——看 await(每次 IO 平均耗时)和队列深度更可靠。

bash
iostat -xz 1        # await、aqu-sz(队列深度)、%util
pidstat -d 1        # 哪个进程在产生 IO
iotop -o            # 实时 IO 排名

网络/锁方向 ​

延迟升高但系统资源全部空闲 → 应用层问题。网络:ss -ti 看 RTT 和重传(远端慢还是本端慢)、conntrack 表满(nf_conntrack: table full 的 dmesg)、TIME_WAIT 堆积。锁/依赖:perf lock(内核锁)、应用级的连接池/线程池指标、off-cpu 分析(火焰图看"不在 CPU 上的时间花在哪"——pidstat 的 %wait 列、eBPF 的 offcputime)。

定期复查的工具箱 ​

一次性排查靠上述命令组合;持续防护靠基础设施:

eBPF 类工具把"卡顿时刻在干什么"的可见性推到了新高度——biolatency(IO 延迟直方图)、runqlat(调度延迟——进程等多久才上 CPU)、offcputime(off-CPU 时间火焰图)。卡顿是"延迟毛刺"类问题时,直方图的尾部分布比平均值敏感得多。

trace 类的最后手段:卡顿仍然无法定位时,perf record -g 定时采样 + 卡顿时刻对比,或者 ftrace 的 irqsoff/preemptoff tracer(关中断/关抢占最长的时段——延迟毛刺的内核侧来源)。这类工具开销大,只在定向排查时短时开启。

排查笔记的习惯:"莫名卡顿"往往最终定位到朴素的根因——备份任务与业务高峰重叠、cron 每小时的日志压缩、某个容器的日志打满磁盘、swap 分区在一个"以为没有 swap"的机器上。系统性方法的价值不是把简单问题复杂化,而是确保简单原因被系统性排除——按 CPU/内存/IO/网络/锁的顺序过一遍,简单原因自然浮出。