云服务器CPU占用率过高怎么办?排查原因与优化方法

云服务器 CPU 占用率突然升高时,最常见的处理误区是立即重启或直接杀死排名第一的进程。这样可能暂时恢复服务,却会丢失定位现场,也可能中断正在执行的订单、任务或数据库写入。正确做法是先判断高占用是否异常,再确定是哪个进程、哪个线程以及哪类代码路径消耗了计算资源。

这篇文章给出一套适用于 Linux 云服务器的排查流程,覆盖 top、htop、ps、pidstat、mpstat 和 perf。命令会尽量使用短时采样,降低对生产环境的影响。涉及终止进程、调整优先级和扩容前,必须先确认业务影响并保留监控、日志与性能样本。

先读懂 CPU 利用率和负载

CPU 利用率不是一个单一指标。top 顶部常见的字段包括用户态 us、系统态 sy、空闲 id、I/O 等待 wa 和虚拟化偷取时间 st。它们对应不同问题:

指标 含义 持续偏高时的常见方向
us 用户态代码消耗 业务计算、压缩加密、死循环、垃圾回收
sy 内核态消耗 高频系统调用、网络包、磁盘或驱动路径
wa CPU 等待块设备 I/O 的时间 磁盘延迟、存储拥塞、同步写较多
st 虚拟 CPU 被 Hypervisor 分给其他实例的时间 宿主机争用或共享型实例资源不足
id 空闲时间 越低说明 CPU 越忙,但要结合其他字段

wa 高时,表面上系统负载可能很高,但根因不一定是计算不足,而可能是磁盘请求迟迟未完成。直接增加 vCPU 可能没有效果。st 持续较高常提示底层争用,但应连续采样并保留证据,再与云服务商确认,不能凭一次尖峰下结论。

负载平均值表示一段时间内处于可运行状态以及不可中断睡眠状态的任务数量,不等于 CPU 百分比。可以先查看:

uptime
nproc
cat /proc/loadavg

四个 vCPU 的服务器如果负载短时达到四,不一定异常;如果长期明显高于核心数,并且请求延迟同步上升,说明任务排队或 I/O 阻塞需要处理。多核服务器上单个进程显示 200%,通常表示它使用了约两个逻辑核心,不是统计错误。

还要先明确业务基线。每天固定时间的报表、备份压缩、日志分析可能会造成可预期峰值。若 CPU 高但吞吐增加、延迟与错误率正常,这可能是有效利用;若吞吐不增反降、超时变多或进程重启,才更接近异常。最好对比同一时段过去七天的监控,而不是只看当前截图。

用 top、htop 和 ps 快速定位进程

先运行 top 观察整体状态:

top

进入后按 P 可按 CPU 排序,按 1 展开每个逻辑 CPU,按 H 切换线程显示,按 c 显示完整命令行。不要只看瞬间第一名,至少观察几十秒,确认是持续占用还是采样尖峰。记录 PID、用户、命令、CPU、内存和运行时间。

若喜欢更直观的界面,可以安装 htop。RHEL、Rocky、CentOS 与 Debian、Ubuntu 的仓库可用性可能不同:

# RHEL、Rocky、CentOS;软件包可能需要已启用的扩展仓库
sudo dnf install -y htop

# Debian、Ubuntu
sudo apt update
sudo apt install -y htop

运行:

htop

htop 适合交互查看树状进程和各核心占用,但生产排障仍应保存可复制的文本证据。可以用 ps 生成一次性快照:

ps -eo pid,ppid,user,stat,comm,%cpu,%mem,etime,args --sort=-%cpu | head -n 20

%CPU 是进程在统计窗口中的使用情况,短命进程可能在一次快照中消失。若看到大量相同命令,应进一步确认它们是正常工作进程、任务风暴还是反复崩溃重启。结合进程树可判断父子关系:

ps -ef --forest

定位 PID 后,先确认可执行文件、启动参数、工作目录和打开文件,不要立即终止:

PID=1234
sudo readlink -f /proc/$PID/exe
sudo tr '\0' ' ' < /proc/$PID/cmdline; echo
sudo readlink -f /proc/$PID/cwd
sudo ls -l /proc/$PID/fd | head

把 1234 替换为真实 PID。若进程由 systemd 管理,还应找到对应服务并查看日志:

systemctl status SERVICE_NAME --no-pager
journalctl -u SERVICE_NAME --since '-30 min' --no-pager

未知进程不一定是恶意程序,也可能是发行版代理、监控组件或容器工作负载。应核对软件包来源、父进程和部署记录。如果命令路径位于临时目录、名称异常且持续联网,再按安全事件流程隔离与取证,不能只删除文件后继续运行。

用 pidstat、mpstat 定位持续占用和热线程

pidstat 与 mpstat 来自 sysstat 软件包,适合按固定间隔采样。安装命令如下:

# RHEL、Rocky、CentOS
sudo dnf install -y sysstat

# Debian、Ubuntu
sudo apt update
sudo apt install -y sysstat

先观察所有进程,每秒一次,共十次:

pidstat -u -p ALL 1 10

输出中的 %usr、%system、%wait 可以帮助区分用户态、系统态与等待。和 ps 的单次快照相比,多次采样更容易发现周期任务与短时进程。已经知道 PID 时可只盯目标:

pidstat -u -r -d -p 1234 1 10

这会同时观察 CPU、内存缺页和 I/O。若进程 CPU 很高,但同时存在大量磁盘活动,应避免把问题简单归类为计算瓶颈。

多线程应用需要定位到线程:

top -H -p 1234
pidstat -t -u -p 1234 1 10

Linux 工具显示的线程 ID 通常是十进制。Java 线程转储中的 nid 常是十六进制,可以把热点线程 ID 转换后在转储里匹配:

printf '0x%x\n' 线程十进制ID

对 Java 应用,可在确认 JDK 工具可用且不会造成额外风险后,短时连续获取线程栈,观察热点线程是否反复停在同一调用路径。对 Go、Python、Node.js 等运行时,应优先使用其官方性能分析工具或应用自带分析接口,不要只根据进程名猜测代码问题。

检查各逻辑 CPU 是否均衡:

mpstat -P ALL 1 10

如果只有一个核心长期接近满载而其他核心空闲,常见原因包括单线程计算、锁竞争、事件循环阻塞或 CPU 亲和性限制。增加核心对单线程瓶颈帮助有限,应优化热点算法、拆分任务或增加进程数。如果所有核心都繁忙,则更可能是总体请求量、并行任务或后台计算超过容量。

在云服务器和容器环境中还要检查配额。容器可能因为 CPU limit 被节流,即使宿主机仍有空闲。systemd 服务也可能设置 CPUQuota。可以查看:

systemctl show SERVICE_NAME -p CPUQuotaPerSecUSec -p AllowedCPUs

容器平台的指标和控制组版本差异较大,应从编排平台查看 CPU 请求、限制和 throttling 指标。不要在不知道调度规则的情况下直接删除限制,否则单个服务可能抢占整台主机。

用 perf 分析用户态和内核态热点

当前面的工具只能确认“某进程很忙”,却不能解释“忙在哪里”时,可以使用 perf。它基于 Linux 性能计数器采样函数调用,适合定位热点函数、系统调用和内核路径。生产环境应缩短采样时间并控制频率,先在测试环境复现;权限和内核参数也可能限制使用。

RHEL、Rocky、CentOS 通常安装:

sudo dnf install -y perf

Debian 可安装发行版提供的 linux-perf,Ubuntu 常使用与当前内核匹配的工具包:

# Debian
sudo apt update
sudo apt install -y linux-perf

# Ubuntu
sudo apt update
sudo apt install -y linux-tools-common linux-tools-$(uname -r)

先进行实时短时观察:

sudo perf top -p 1234

如果需要保留样本,可以用较保守的九十九赫兹频率采集三十秒调用栈:

sudo perf record -F 99 -g -p 1234 -- sleep 30
sudo perf report

采样文件默认是当前目录的 perf.data,可能包含函数名和业务路径信息,应按敏感运维数据保管。采样本身有开销,问题越严重越不能无限提高频率或长时间运行。先从短窗口开始,观察服务器延迟和负载,必要时停止。

热点解读需要结合程序符号。如果报告大量显示未知符号,可能缺少调试符号、二进制被裁剪,或者运行时需要专门的栈展开支持。不要因为某个内核函数排在前面就直接认定内核故障,它可能只是业务大量调用某类系统调用的结果。

常见模式包括:压缩与加密函数高,说明请求内容或 TLS 计算较重;正则或序列化函数高,可能存在低效数据处理;内存分配函数高,可能创建对象过多;锁与调度函数高,可能存在竞争;网络或文件系统路径高,则要结合包速率和磁盘指标继续排查。

perf 是定位手段,不是修复方法。最终仍需回到代码、配置和负载模型。例如,同一函数在流量翻倍后占用翻倍可能是容量问题;流量不变却突然升高,则更像数据分布变化、错误重试或版本回归。

按根因止损并做长期优化

流量增长或恶意请求

先对比访问日志、每秒请求数、来源分布、状态码和接口延迟。若某个接口被集中调用,可在负载均衡、网关或 Nginx 层做合理限流与缓存。不要仅按单个地址粗暴封禁全部流量,因为代理和共享出口可能让正常用户使用同一地址。对于真实业务增长,应增加无状态应用实例并通过负载均衡分流。

代码死循环、任务风暴或错误重试

查看最近发布、定时任务和消息队列积压。错误重试没有退避时,一个下游故障可能触发大量空转。修复策略包括为重试增加上限与退避、限制工作线程、对失败任务进入死信队列,并在测试环境复现热点。回滚版本前应确认数据库结构兼容,不能只替换二进制。

若必须停止异常进程,先通过服务管理器执行优雅停止,或发送 SIGTERM:

sudo systemctl stop SERVICE_NAME
# 或仅在确认 PID 后
sudo kill -TERM 1234

风险提示:终止进程会中断请求和未完成任务。先确认是否有多副本接管、是否需要摘除流量,并保存日志与性能样本。不要上来就用 kill -9,它不给程序清理临时文件、刷新缓冲和释放锁的机会。只有进程无法响应正常终止且业务负责人确认后,才考虑强制手段。

数据库与缓存拖累应用 CPU

应用忙等数据库、重复序列化大结果集或缓存失效,也可能让 CPU 上升。检查慢查询、连接池、缓存命中率与返回数据大小。为热点 SQL 增加合适索引、减少无用列、分页读取,并避免在应用端反复处理巨量结果。不能通过无限增加连接数解决吞吐,连接竞争反而可能进一步提高系统态和上下文切换。

日志、压缩和后台任务争用

过高日志级别会带来格式化、系统调用和磁盘开销。生产环境不要长期启用调试日志,批量压缩、备份和报表任务应错峰运行。可以在确认业务优先级后降低后台任务调度优先级:

sudo renice 10 -p 1234

数值越大,普通 CPU 调度优先级越低。调整前记录原值,使用 ps -o pid,ni,comm -p 1234 验证。renice 只能缓解争抢,不能修复死循环或容量不足,也不能保证实时服务一定获得资源。

底层争用或规格不足

如果 st 在业务高峰持续偏高,而实例内部没有对应进程增长,应保留 mpstat、vmstat、云监控和业务延迟证据,联系服务商排查宿主机争用。可评估迁移到性能保障更明确的实例规格。具体价格和能力以官网为准。

如果所有核心长期繁忙且吞吐随 CPU 接近线性增长,说明容量可能确实不足。无状态服务优先横向扩展,有状态单机则先确认锁、I/O 和单线程瓶颈,再考虑升配。扩容后重复相同压测,验证性能是否按预期改善。

长期应设置分层告警:短时峰值只记录,持续高占用才告警,并同时关联延迟、错误率和吞吐。部署发布标记、定时任务记录与容量趋势,能让下一次排查从“谁在用 CPU”快速推进到“哪个变更导致 CPU 变化”。

常见问题

CPU 达到百分之百一定要扩容吗?

不一定。先确认持续时间、吞吐和延迟。短时批处理用满 CPU 可能正常;死循环、错误重试或单线程瓶颈也无法靠简单加核根治。只有容量分析证明资源不足时再扩容。

top 中 wa 很高,是 CPU 不够吗?

通常不是。wa 表示等待块设备 I/O 的时间,应继续检查磁盘延迟、队列和数据库写入。增加 CPU 往往不能解决存储瓶颈。

为什么单个进程显示超过百分之一百?

在多核 Linux 中,top 常把一个逻辑核心记为百分之一百。多线程进程同时使用两个核心时可显示约百分之二百,属于正常统计方式。

重启能不能作为应急方案?

可以作为经过批准的最后止损手段,但应先保存进程、日志和性能样本,并确认重启后的流量接管与数据一致性。重启不会修复代码缺陷或资源超售,问题通常还会复发。

perf 可以直接在生产环境长时间运行吗?

不建议。先用短时间、较低频率采样,观察开销,并优先在测试环境复现。采样数据也可能包含敏感函数与路径信息,需要限制访问和及时清理。

总结

  • CPU 排障先区分用户态、系统态、I/O 等待与虚拟化偷取时间,再结合核心数和业务基线判断是否异常。
  • 使用 top、htop、ps 找进程,用 pidstat、mpstat 连续采样并定位线程,避免凭一次快照下结论。
  • perf 适合分析函数热点,但生产采样要控制频率和时长,并妥善保管性能数据。
  • 停进程、回滚和扩容前先保存现场、摘除流量并确认数据影响,不要把重启或 kill -9 当作默认修复。
  • 长期优化应针对流量、代码、数据库、后台任务或底层争用的真实根因,并用延迟、错误率和吞吐验证效果。