4.6.6 高负载问题排查与恢复方案

在生产环境中出现 CPU 高负载,需要先区分“负载高”与“CPU 高使用率”的差异。负载(Load Average)反映可运行队列长度与不可中断任务数量,可能由 CPU 密集、I/O 阻塞、锁竞争等引起。本节给出快速判定、定位、止损、修复与验证的闭环流程,并配套可执行命令与练习。

原理草图:高负载来源与排查路径#

文章图片

一、快速判定与影响面评估#

示例:采集负载与CPU占比

# 1) 观察 Load Average 与 CPU 使用率
uptime
top -b -n 1 | head -n 15

# 2) 查看 CPU 关键字段(us/sy/wa/st)
mpstat -P ALL 1 3

# 预期:若 wa 高,I/O 可能阻塞;若 st 高,虚拟化抢占。

命令解释
- uptime:输出最近 1/5/15 分钟平均负载。
- topus用户态,sy内核态,waI/O等待,st虚拟化被抢占。
- mpstat -P ALL 1 3:每 1 秒采样 3 次,每核输出。

二、工具安装与准备#

安装常用排查工具(CentOS/RHEL)

sudo yum install -y sysstat iotop perf psmisc

安装常用排查工具(Ubuntu/Debian)

sudo apt-get update
sudo apt-get install -y sysstat iotop linux-tools-common linux-tools-$(uname -r) psmisc

启用 sysstat 采集

sudo systemctl enable --now sysstat

三、定位高负载进程与线程#

示例:锁定高 CPU 进程与线程堆栈

# 1) 找到高CPU进程
ps -eo pid,ppid,cmd,%cpu,%mem --sort=-%cpu | head -n 10

# 2) 观察某进程内线程
PID=1234
top -H -p $PID

# 3) 将线程ID转为16进制以便堆栈匹配(Java示例)
TID=5678
printf "0x%x\n" $TID

Java 线程堆栈定位

# 获取堆栈
jstack -l $PID > /tmp/jstack.$PID.txt

# 在堆栈中查找线程ID(16进制)
grep -n "0x" /tmp/jstack.$PID.txt | head

非 Java 进程堆栈(C/C++)

# pstack 来自 gdb
sudo pstack $PID | head -n 50

四、识别 I/O 与外部依赖阻塞#

示例:I/O 与网络检查

# I/O 等待与磁盘队列
iostat -xz 1 3

# 进程级 I/O
sudo iotop -o -b -n 3

# 网络连接与积压
ss -s
ss -tnp | head -n 20

命令解释
- iostat -xzawait 过高说明磁盘响应慢,svctm 低但 await 高多为排队。
- iotop -o:仅显示有 I/O 活动进程。
- ss -s:查看连接状态汇总,排查半连接、TIME_WAIT 等堆积。

五、内核与系统级异常确认#

示例:内核错误与热点函数

# 内核异常日志
dmesg -T | tail -n 50
journalctl -k -n 50

# 性能热点
sudo perf top

# 采样 30 秒生成报告
sudo perf record -F 99 -p $PID -g -- sleep 30
sudo perf report

六、临时恢复与止损策略(可执行)#

示例:降级/限流与资源隔离

# 1) 调整进程优先级(降低非关键任务)
sudo renice +10 -p $PID

# 2) 临时限制CPU占用(cpulimit需安装)
sudo cpulimit -p $PID -l 50

# 3) 暂停非关键批处理
sudo systemctl stop batch-job.service

# 4) 使用 cgroup 限制CPU(示例)
sudo mkdir -p /sys/fs/cgroup/cpu/lowprio
echo 50000 | sudo tee /sys/fs/cgroup/cpu/lowprio/cpu.cfs_quota_us
echo 100000 | sudo tee /sys/fs/cgroup/cpu/lowprio/cpu.cfs_period_us
echo $PID | sudo tee /sys/fs/cgroup/cpu/lowprio/cgroup.procs

预期效果
- 高负载进程 CPU 降到指定限制内,系统 Load Average 逐步回落。
- 关键业务获得更多 CPU 配额。

七、根因修复与优化(示例 + 参数)#

示例:线程池与 GC 调优(Java)

# 应用启动参数示例(减少频繁GC与过度线程争用)
JAVA_OPTS="-Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/app/gc.log \
-Djava.util.concurrent.ForkJoinPool.common.parallelism=4"

# 启动服务(示例)
/opt/app/bin/start.sh "$JAVA_OPTS"

示例:内核调度参数(谨慎测试)

# 降低过度换出,提高运行队列调度稳定性
sudo sysctl -w vm.swappiness=10
sudo sysctl -w kernel.sched_autogroup_enabled=0

说明
- 参数改动必须在测试或灰度环境验证;记录基线与回滚步骤。

八、验证与复盘#

示例:恢复验证

# 观察负载与CPU
uptime
mpstat -P ALL 1 3

# 验证业务指标(示例)
curl -s http://127.0.0.1:8080/health

复盘输出清单
- 触发条件、影响范围、关键日志、处理步骤、根因与改进动作。

九、常见故障排错清单#

  1. 负载高但 CPU 低:多为 I/O 或锁等待,检查 waiostat
  2. 单核 100%:应用单线程热点,检查 top -H 与堆栈。
  3. st 高:虚拟化抢占,联系平台扩容或迁移宿主机。
  4. sy 高:系统调用密集或内核驱动问题,结合 perf top 与内核日志。

十、练习#

  1. 使用 stress-ng 制造 CPU 负载并定位热点:
# 安装
sudo yum install -y epel-release && sudo yum install -y stress-ng
# 启动 4 个 CPU 压力进程
stress-ng --cpu 4 --timeout 60
# 观察负载与CPU占比
uptime && mpstat -P ALL 1 3
  1. 使用 perf 采样并生成报告:
# 对 stress-ng 进程采样
PID=$(pgrep -n stress-ng)
sudo perf record -F 99 -p $PID -g -- sleep 10
sudo perf report
  1. 使用 cgroup 限制 CPU 并验证生效:
PID=$(pgrep -n stress-ng)
sudo mkdir -p /sys/fs/cgroup/cpu/limit
echo 50000 | sudo tee /sys/fs/cgroup/cpu/limit/cpu.cfs_quota_us
echo 100000 | sudo tee /sys/fs/cgroup/cpu/limit/cpu.cfs_period_us
echo $PID | sudo tee /sys/fs/cgroup/cpu/limit/cgroup.procs
top -p $PID

通过标准化排查流程与分层恢复方案,可在高负载场景下实现“快速止损—准确定位—持续优化”的闭环处理。