返回首页

服务器突然卡顿运维排查 SOP — 从告警到根因的完整取证指南

📅 创建于 2026-07-14 🔄 更新于 2026-07-14 📝 1085 字

服务器突然卡顿运维排查 SOP

来源:运维派 | 发布日期:2026-07-13 原文:https://mp.weixin.qq.com/s/BskVYSGQ4GpU52njG7ZPvw

本文是 全量排查流程篇,覆盖从卡顿告警到根因确认的完整取证路径。与 server-performance-four-dimensions(五维深度解析)和 cpu-spike-troubleshooting-guide(CPU 专项)互补使用。


一、先明确"卡顿"是什么

收到告警后,先记录完整信息:

  • 首次发生时间、持续时长、是否已恢复
  • 影响范围:整机、一个服务、一个接口,还是一批客户端
  • SSH 是否也慢,还是只有业务请求慢
  • 是否最近有发布、扩容、批处理、备份、安全扫描

故障开始时间很关键。只看当前状态可能得出"一切正常"的结论,而 sar、日志和监控可以回看已结束的尖峰。

排查时最容易犯的错误,是一登录就重启服务、清缓存或修改内核参数。这样可能暂时恢复业务,却会同时清掉进程现场、短周期指标和部分日志证据。本文命令适用于 RHEL、Rocky Linux、AlmaLinux、Ubuntu、Debian;个别工具需要安装 sysstat、iotop、lsof、ethtool 等软件包。不同发行版、内核和工具版本的字段可能略有差异,以本机帮助信息和实际输出为准。

二、信息收集:先做只读快照

DIAG_DIR="/var/tmp/diag-$(hostname)-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$DIAG_DIR"
uptime | tee "$DIAG_DIR/uptime.txt"
free -h | tee "$DIAG_DIR/free.txt"
vmstat 1 10 | tee "$DIAG_DIR/vmstat.txt"
chmod 700 "$DIAG_DIR"

关键原则: 登录后先执行低风险只读命令,不要把采集文件写到已爆满的分区。系统接近失去响应时,不要同时启动多项高频采集。

三、初步判断:vmstat 区分方向

vmstat 1 10

关键指标解读

指标 含义 异常信号
r 等待 CPU 的可运行任务数 持续 > CPU 核数 → CPU 排队
b 不可中断睡眠任务数 I/O 等待,需结合进程状态确认
si/so 每秒换入/换出量 持续非零说明活跃交换
us/sy 用户态/内核态 CPU us 高→应用计算;sy 高→系统调用/内核
wa CPU 空闲时等待 I/O 比例 不是磁盘利用率,wa 低也不能排除设备延迟
st 虚拟机被宿主机拿走的 CPU 云主机关键,持续升高联系云平台
# 确认逻辑 CPU 数
nproc
lscpu

四、CPU 排查

诊断关键

# 查看整体和单核使用率
mpstat -P ALL 1 5
# 示例输出:
# 14:35:01 CPU  %usr %nice %sys %iowait %irq %soft %steal %idle
# 14:35:02 all 72.10  0.00 18.30    0.20 0.10  5.20   0.00  4.10
# 14:35:02   0 95.00  0.00  3.00    0.00 0.00  1.00   0.00  1.00

解读: %usr 高通常是应用计算;%sys 高可能与系统调用、锁、网络包或内存管理有关;%soft 高常见于网络软中断;单核满而其他核心空闲可能是单线程热点或中断亲和性问题。

定位进程和线程

# 进程级
pidstat -u 1 10
ps -eo pid,ppid,user,stat,psr,pcpu,pmem,etime,comm,args --sort=-pcpu | head -30
# 示例输出:
# 14:36:09 UID   PID  %usr %system %guest %wait  %CPU CPU Command
# 14:36:09 998 18472 165.0    28.0   0.0   2.0 193.0   6 java
# 多线程进程 %CPU 可超过 100%

# 线程级
top -H -p 18472
pidstat -t -p 18472 1 10
# PID 会复用,执行前通过 ps -p 18472 -o pid,lstart,args 再确认进程身份

系统态与软中断

%sys%soft 高时:

sar -w 1 10          # 上下文切换
cat /proc/softirqs   # 软中断分布
ethtool -S eth0      # 网卡统计
pidstat -w 1 10      # 自愿/非自愿切换

锁竞争与线程池

进程可能只占少量 CPU,但因锁或线程池耗尽而几乎不工作。表现:请求排队、活动线程固定、吞吐下降,整机仍有大量空闲 CPU。

timeout 10 strace -f -p <PID> -tt -T -c

大量 futex 只说明线程同步活动多,不能证明某把锁是根因。Java 用 jcmd,Go 用 pprof,数据库用自身等待事件。

五、内存排查

free -h
grep -E 'MemAvailable|SwapTotal|SwapFree|Dirty|Slab|SReclaimable' /proc/meminfo
vmstat 1 10
检查点 说明
available Linux 用空闲做页缓存,不能只看 free
si/so 持续 只有活跃交换才说明内存压力
OOM 日志 journalctl -k \| grep -Ei 'out of memory|oom-killer'
PSI 压力 cat /proc/pressure/memory
内存大户 ps -eo pid,user,rss,vsz,pmem,etime,comm --sort=-rss \| head -30
Slab 目录项/inode 缓存大可能来自扫描小文件
NUMA numactl --hardware + numastat 检查节点局部压力

容器环境检查 cgroup:

cat /sys/fs/cgroup/memory.current
cat /sys/fs/cgroup/memory.max
cat /sys/fs/cgroup/memory.events

区分页缓存、Slab 和匿名内存

类型 含义 异常信号
AnonPages 匿名页(进程堆/栈) 高通常与进程堆、匿名映射有关
Slab 内核对象缓存 SReclaimable 理论上可回收;SUnreclaim 不可直接回收
PageTables 页表内存 大内存映射场景可能显著增长
Dirty/Writeback 脏页 持续大量可能说明 IO 堵塞
grep -E '^(AnonPages|Mapped|Shmem|Slab|SReclaimable|SUnreclaim|PageTables|Dirty|Writeback):' /proc/meminfo
slabtop -o

在 NUMA 服务器上,系统总内存有余也可能出现节点局部压力:

numactl --hardware
numastat
numastat -p <PID>

不要使用 echo 3 > /proc/sys/vm/drop_caches 作为常规修复。它会丢弃可回收缓存并导致后续大量读取,且不能解决泄漏。

六、磁盘与文件系统

df -hT
df -i
iostat -xz 1 10
pidstat -d 1 10
iotop -oPa
findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS

关注 r_awaitw_await、队列长度,并与设备类型和历史基线比较。%util 对 NVMe/多队列设备不能简单解释为饱和。

# 检查块设备错误
journalctl -k | grep -Ei 'I/O error|timeout|reset|nvme|ext4|xfs|nfs'
# 已删除但仍占用的文件
lsof +L1

七、网络排查

ip -s link
sar -n DEV 1 10
sar -n TCP,ETCP 1 10
ss -s
ethtool eth0 | grep -E 'Speed|Duplex|Link detected'
nstat -az | grep -E 'TcpRetransSegs|TcpExtListenOverflows|TcpExtListenDrops'

连接状态和热点对端:

ss -tan state established | awk 'NR>1 {print $5}' | sort | uniq -c | sort -nr | head

抓包需授权,限定接口、端口和包长:

timeout 60 tcpdump -i eth0 -nn -s 128 -c 20000 'host 10.x.x.x and port 3306' -w /var/tmp/db-traffic.pcap
chmod 600 /var/tmp/db-traffic.pcap

八、应用与依赖排查

系统没满不代表服务没堵。常见信号:

  • 工作线程池 active 达到上限,队列持续增长
  • 数据库连接池等待时间和超时上升
  • GC 暂停、锁竞争或事件循环阻塞
  • 日志同步写、备份、定时任务与故障窗口重合
systemctl status example.service --no-pager
journalctl -u example.service --since "2026-07-13 14:00:00" --until "2026-07-13 15:00:00" --no-pager

关键判断: "并发上限"和"处理能力"要分开。一个连接池只有 20 个连接时,超过 20 个并发请求仍需排队。扩大连接池可能把排队从应用转移到数据库。

K8s 环境:

kubectl -n <namespace> get pod -o wide
kubectl -n <namespace> top pod
kubectl -n <namespace> logs <pod-name> --since=30m --timestamps

不要在排查阶段直接执行删除 Pod、缩扩容或节点驱逐。

九、故障已恢复:用 sar 还原现场

# 查看 sysstat 历史文件
ls -lh /var/log/sa /var/log/sysstat 2>/dev/null

# 回看故障窗口(替换路径和时间)
sar -u -f /var/log/sa/sa13 -s 14:20:00 -e 14:35:00
sar -q -f /var/log/sa/sa13 -s 14:20:00 -e 14:35:00
sar -r -S -f /var/log/sa/sa13 -s 14:20:00 -e 14:35:00
sar -d -p -f /var/log/sa/sa13 -s 14:20:00 -e 14:35:00
sar -n DEV,TCP,ETCP -f /var/log/sa/sa13 -s 14:20:00 -e 14:35:00

检查系统时间是否跳变:

timedatectl status
journalctl -u chronyd -u systemd-timesyncd --since "2026-07-13 14:00:00" --until "2026-07-13 15:00:00" --no-pager

十、资源不高但请求卡住:文件描述符、端口、DNS

文件描述符耗尽

SERVICE_PID=$(systemctl show -p MainPID --value example.service)
cat "/proc/$SERVICE_PID/limits" | grep "open files"
find "/proc/$SERVICE_PID/fd" -maxdepth 1 -type l | wc -l
lsof -nP -p "$SERVICE_PID" | awk '{print $5}' | sort | uniq -c | sort -nr

端口与连接跟踪

sysctl net.ipv4.ip_local_port_range
cat /proc/sys/net/netfilter/nf_conntrack_count 2>/dev/null
cat /proc/sys/net/netfilter/nf_conntrack_max 2>/dev/null

DNS 与 TLS 延迟

/usr/bin/time -f '%e seconds' getent hosts api.example.internal

# TLS 分阶段耗时
curl --output /dev/null --silent --max-time 10 \
  --write-out 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} first_byte=%{time_starttransfer} total=%{time_total}\n' \
  https://service.example.com/health

十一、虚拟机与容器限额

云主机中 %steal 持续升高说明宿主机争用。容器内 CPU 数与配额可能不一致:

# cgroup v2
cat /sys/fs/cgroup/cpu.max
cat /sys/fs/cgroup/cpu.stat | grep throttled
cat /sys/fs/cgroup/memory.events
# cgroup v1 路径不同,以 mount | grep cgroup 为准

CPU throttling 频繁增长 + 延迟升高 + 节点仍有空闲 → 容器限额可能是瓶颈。

十二、证据链确认根因

根因需满足三个一致:

  1. 时间一致 — 指标、日志、作业记录的时间窗口重合
  2. 对象一致 — 同一进程/设备/任务
  3. 机制一致 — 因果链完整(如:批处理 → 写盘 → w_await 升高 → 接口延迟上升)

示例完整证据链:"接口 P99 在 14:27 升高 → iostat 显示同一窗口 w_await 高于基线 → pidstat 显示批处理进程是主要写入者 → 作业平台确认任务 14:27 启动 → 停止单灰度作业后延迟恢复。"

十三、修复:先止损,再处理根因

低风险止损动作:暂停非核心批任务、摘除异常实例、降低后台并发、关闭刚启用的功能开关。

服务重启按高风险操作处理:

# 验证
systemctl cat example.service
systemd-analyze verify /etc/systemd/system/example.service
# 重启
systemctl restart example.service
systemctl is-active example.service
# 验证后观察至少一个业务周期

修改内核参数同样是高风险操作。不要在故障现场直接 sysctl -w 放大队列或关闭交换。

十四、验证四层

修复后至少完成:

检查项
系统层 负载、CPU、内存压力、磁盘延迟、丢包回到基线
进程层 异常线程恢复、队列不再增长
应用层 吞吐恢复、P50/P95/P99 延迟符合目标
用户层 从入口发起受控请求确认关键链路可用

十五、可执行排查顺序

☐ 明确故障窗口、影响范围、最近变更
☐ 保存监控,记录 date、uptime、登录
☐ vmstat 判断 CPU/I/O/交换方向
☐ mpstat + pidstat + ps 定位 CPU 进程/线程
☐ free + meminfo + PSI + OOM 日志检查内存
☐ df + iostat + pidstat -d + 内核日志检查磁盘
☐ ip -s + sar + nstat + ss 检查网络
☐ 检查线程池、连接池、GC、日志、下游依赖
☐ 建立时间+对象+机制一致的证据链
☐ 单实例灰度止损,持续验证,满足条件回退
☐ 复盘,补齐监控/限流/容量/变更保护

关联页面

页面关联点
server-performance-four-dimensionsCPU/内存/磁盘/网络/文件系统五维深度解析
cpu-spike-troubleshooting-guideLinux CPU 飙高排查完整方法论
cpu-100-full-chain-diagnosisCPU 100% 全链路分析与 10 大场景实战
linux-perf-troubleshooting-handbookLinux 性能排查实战手册(三板斧/阈值速查)
fullstack-performance-troubleshootingNginx → 应用 → 数据库 → 服务器全栈排障
linux-disk-io-troubleshoot磁盘 IO 排查实战 — %util 陷阱与 await 真相。服务器变慢时 CPU/内存正常但磁盘
system-load-high-rsyslogd-case-study一个 rsyslogd 被 audit 审计日志刷屏导致的系统负载高案例,涵盖 CPU 拆解、软中断
nginx-log-analysis-troubleshooting-guideNginx 日志分析与排障