CPU 100%告警反复出现,怎样判断该调内核参数还是修复应用?
CPU 100%告警本身只能说明“CPU时间被大量消耗”,不能直接证明是内核参数不足,也不能直接证明应用代码有问题。先看这100%由 user、system、iowait、softirq 还是 steal 构成,再确认是单个进程、单个线程、数据库进程,还是整台主机共同升高,才能决定后续动作。
排查顺序应当是:先确认告警是否持续并保留现场,再区分CPU、内存、磁盘和网络压力;随后沿着进程、线程、应用请求和数据库语句逐层溯源。只有当系统指标明确指向连接队列、文件描述符、连接跟踪表等内核资源耗尽时,才考虑调内核参数。若某个应用线程、接口、定时任务或SQL持续消耗CPU,优先修复应用或查询,调参通常只是延后再次告警。
先确认“100%”到底代表什么
不同监控工具对CPU百分比的统计口径可能不同。整机CPU使用率达到100%,通常表示所有逻辑CPU的总计算能力基本被占满;而 top 中某个进程显示300%,可能表示它同时使用了约3个逻辑CPU。因此,不能只看告警标题或单个进程百分比。
先记录告警时间、持续时长、受影响的业务接口、部署变更和定时任务。排查时尽量不要一开始就重启服务、清理缓存或修改系统参数,否则可能丢失最有价值的现场信息。
可以先执行以下低风险检查:
date
uptime
nproc
top -b -n 1 | head -n 25
重点关注以下指标:
| 指标 | 主要观察内容 | 常见含义 |
|---|---|---|
| load average | 与逻辑CPU数量比较,观察是否持续增长 | 可能有CPU运行队列,也可能包含不可中断的磁盘等待 |
%us / user | 用户态程序消耗的CPU | 应用代码、数据库计算、压缩、加密、垃圾回收等 |
%sy / system | 内核态消耗的CPU | 系统调用、网络栈、文件系统、内存管理等 |
%wa / iowait | 等待磁盘或块设备完成I/O的时间 | 磁盘延迟、查询I/O、日志写入或内存回收 |
%si / softirq | 软件中断消耗的CPU | 网络包处理、连接风暴、数据包转发等 |
%hi / irq | 硬件中断消耗的CPU | 网卡、存储设备或其他硬件中断压力 |
%st / steal | 虚拟机被宿主机暂时收回的CPU时间 | 宿主机争用或虚拟化层资源不足 |
| 单个线程CPU | 是否只有一个线程长期占满一个核心 | 单线程计算、锁竞争、循环或某个请求处理异常 |
load average 不能单独等同于CPU使用率。例如CPU使用率不高,但磁盘等待严重时,负载仍可能很高;反过来,多个线程持续计算时,CPU和负载可能同时升高。
按优先级保留现场
第一步:确认是尖峰还是持续性问题
如果CPU只在几秒内达到100%,随后恢复,常见原因包括定时任务、日志压缩、批量请求、缓存重建或监控采集。若告警反复出现,且每次都与固定时间、固定接口或固定任务相关,应优先查业务触发条件。
可以连续采集几组数据:
vmstat 1 5
mpstat -P ALL 1 5
pidstat -u -r -d -w -t 1 5
这些命令通常来自 procps 或 sysstat 工具包。如果命令不存在,不要直接猜测安装包名称,先确认系统发行版和软件源,再安装对应工具。
结果可以这样理解:
mpstat显示所有核心都接近繁忙,且%us较高:倾向于应用或数据库的实际计算压力。- 只有一个核心接近100%,其他核心空闲:可能是单线程任务、线程绑定、锁竞争或应用并行度不足。
%sy、%si明显升高:转向系统调用、网络包处理、文件系统或连接风暴。%wa较高:不要把它当成“CPU不够”,应继续查磁盘、内存和I/O路径。%st较高:虚拟机内部调参通常不能解决宿主机CPU争用。vmstat的r长期高于逻辑CPU数量:运行队列可能积压;但仍需结合CPU各状态判断。vmstat的b较高:有较多任务处于不可中断等待,常与磁盘或其他I/O有关。
第二步:确认是整机问题还是单一进程问题
使用进程和线程维度继续缩小范围:
ps -eo pid,ppid, user,stat,pcpu,pmem,etime,comm,args --sort=-pcpu | head -n 20
如果已经找到进程PID,例如 12345,可以进一步查看线程:
top -H -p 12345
pidstat -p 12345 -u -r -d -w -t 1 5
这里的关键不是只看“哪个进程排在第一位”,而是观察:
- 该进程是否在告警开始后才升高;
- CPU消耗是否集中在一个线程;
- 进程的内存、读写和上下文切换是否同步异常;
- CPU下降时,业务延迟是否也恢复;
- 进程是否属于应用、数据库、日志组件或系统服务。
若某个应用进程长期占用大部分CPU,而磁盘、网络和内存压力并不明显,优先进入应用溯源;若数据库进程占用CPU,则要进一步看活动会话和慢查询,而不是先调Linux内核参数。
先分辨CPU、内存、磁盘和网络瓶颈
分支一:user CPU高,优先查应用或数据库
%us 较高通常表示程序真正消耗了计算资源。典型原因包括:
- 请求量增加,但单次请求计算量没有下降;
- 正则匹配、序列化、压缩、加密或图片处理耗时;
- 代码出现高频循环、重复计算或异常重试;
- 垃圾回收频繁;
- 定时任务与在线请求同时运行;
- 数据库执行了高成本排序、聚合、全表扫描或大量连接处理;
- 线程数增加后发生竞争,导致上下文切换和无效工作。
此时应将CPU进程与业务指标对齐。至少同时看请求量、响应时间、错误率、并发数和发布记录。如果CPU与请求量同步增加,可能是容量或单位请求成本问题;如果请求量没有增加但CPU突然升高,更像是异常循环、重试、数据分布变化、定时任务或部署引入的回归。
应用侧建议按以下路径确认:
- 根据PID找到服务实例、部署版本和启动参数。
- 根据线程ID确认热点线程属于哪个模块。
- 对照应用日志中的接口、任务名、异常重试和批处理记录。
- 在可控时间窗口使用采样分析工具,确认CPU热点函数。
- 用同等请求量重新验证修复前后的单请求CPU成本。
生产环境不建议未经评估直接使用高开销的全量跟踪或调试器。采样分析通常比持续全量跟踪更适合在线排查;如果必须使用高开销工具,应先限定进程、持续时间和采样频率,并准备停止采集的方法。
分支二:数据库进程CPU高,优先修复SQL和访问模式
当数据库进程占用CPU较高,而应用进程CPU并不高,常见问题可能在数据库查询本身:
- 查询条件选择性差,扫描了大量数据;
- 排序、聚合、连接操作数据量过大;
- 应用产生N+1查询;
- 连接池配置导致并发查询过高;
- 数据量增长后原有索引不再适用;
- 同一条SQL因参数变化出现不同执行计划;
- 失败请求被应用快速重试,形成查询放大。
数据库排查应同时查看活动会话、慢查询记录、执行耗时、扫描行数、锁等待和连接数。不要因为数据库CPU高就盲目增加连接数;连接数增加可能进一步放大CPU、内存和上下文切换压力。
如果数据库CPU并不高,但应用响应时间变长,应重点看数据库等待、磁盘延迟、锁竞争和网络往返,而不是把问题归类为CPU不足。SQL优化也不应只依据一条执行计划就直接改表结构,修改索引或查询前应确认数据量、写入成本、磁盘空间和回滚方案。
分支三:system、softirq或irq高,查系统调用和网络包处理
如果用户态CPU不高,但 %sy、%si 或 %hi 明显升高,方向就从业务计算转向内核路径。常见场景包括:
- 短连接大量创建和关闭;
- 网络连接或数据包数量突然增加;
- 应用频繁调用文件系统、时间函数或进程间通信;
- 大量小包导致软中断压力;
- 日志写入、文件扫描或块设备请求异常;
- 某个服务反复失败并快速重试。
可以先看网络和软中断概况:
sar -n DEV 1 5
ss -s
cat /proc/softirqs
如果系统没有 sar,可以使用网卡计数器和连接统计;如果需要更细的网卡信息,再确认 ethtool 是否可用:
command -v ethtool
ip -s link
这类问题需要区分“内核处理压力”和“应用制造压力”。例如短连接过多,既可能是连接队列或文件描述符不足,也可能是应用没有复用连接、上游超时太短或失败重试过快。只提高队列长度,不能减少连接创建和关闭本身。
分支四:iowait高,查磁盘和内存回收
当 %wa 明显升高时,CPU可能只是等待设备完成工作。建议检查:
iostat -xz 1 5
free -h
vmstat 1 5
df -h
df -i
重点看:
await:I/O请求从提交到完成的平均等待时间;avgqu-sz:设备队列是否积压;%util:设备忙碌程度,但不同设备和内核版本下解释不能机械套用;- 读写吞吐是否接近设备能力;
- 文件系统空间和inode是否耗尽;
- 内存是否紧张并触发频繁回收或交换。
如果磁盘延迟高,同时数据库、日志或应用进程有大量写入,先查具体I/O来源。如果内存不足导致交换,CPU告警可能只是内存压力的后果。此时调整CPU相关参数没有帮助,应先减少内存占用、控制并发、优化缓存或处理I/O瓶颈。
分支五:steal高,查虚拟化资源争用
虚拟机中的 %st 表示虚拟CPU想运行但暂时无法获得宿主机物理CPU。若该值在告警期间持续升高,通常应核查宿主机或云平台的CPU争用、实例配额和资源分配。
此时即使在虚拟机内调整调度参数,也无法凭空增加宿主机提供的计算时间。可以保留虚拟机内的进程和业务证据,再与资源平台侧确认;不要把steal时间误判成应用代码的user CPU。
分支六:网络异常不一定表现为CPU高
网络问题可能导致CPU升高,也可能只表现为响应变慢。需要区分:
- 网络包量过大,导致softirq升高;
- 连接建立过多,导致内核连接表或监听队列压力;
- 丢包和重传导致应用重复发送;
- 上游响应慢,应用线程大量等待;
- 文件描述符或连接池耗尽;
- 应用自身频繁创建短连接。
ss -s 可以帮助查看TCP连接总体状态。若要观察单个连接的更多信息,可以在明确目标连接后使用:
ss -ti
网络工具能说明连接状态和部分TCP统计,但不能证明业务代码没有问题,也不能仅凭连接数多就认定需要调大内核参数。连接数高可能是正常高并发,也可能是连接复用失效或请求超时造成的堆积。
什么时候才应该调内核参数
调参的前提是:已经找到明确的资源边界,并且应用行为本身合理或暂时无法立即修改。一个实用判断是:
- 如果“某个进程或线程在做大量无效计算”,修应用;
- 如果“数据库在执行高成本查询”,修SQL、索引或访问模式;
- 如果“磁盘、内存或虚拟化层在等待”,处理对应资源;
- 如果“请求已经合理,但内核限制导致合法请求被拒绝或排队”,才评估内核参数;
- 如果没有对应的耗尽证据,不要因为CPU告警就批量修改sysctl。
常见参数与判断边界如下:
| 现象 | 需要先确认的证据 | 可能涉及的参数或限制 | 不能忽略的边界 |
|---|---|---|---|
| 监听队列溢出、连接建立阶段排队 | 应用监听队列、内核统计、应用accept能力 | net.core.somaxconn、应用监听backlog | 队列变大不代表应用处理能力变强 |
| 文件描述符耗尽 | 进程打开文件数、ulimit、服务限制 | LimitNOFILE、进程软硬限制、系统文件表 | 提高上限前要确认内存和连接泄漏 |
| 连接跟踪表耗尽 | nf_conntrack_count 接近 nf_conntrack_max,且业务确实使用该机制 | net.netfilter.nf_conntrack_max | 增大表会占用内存,不能解决短连接风暴 |
| 网络缓冲不足 | 丢包、socket buffer不足、吞吐和延迟证据 | net.core.rmem_、net.core.wmem_等 | 缓冲过大可能增加内存占用和排队延迟 |
| 交换或脏页回写造成I/O等待 | 内存压力、交换活动、写回延迟 | vm.swappiness、脏页相关参数 | 不能用它们掩盖内存不足或磁盘过慢 |
| CPU被某个线程持续占满 | 进程和线程热点明确 | 通常不应优先调内核参数 | 调度参数很少能修复业务热循环 |
先查看当前值和相关限制,不要直接复制网络文章中的整套参数:
sysctl net.core.somaxconn
sysctl net.netfilter.nf_conntrack_count 2>/dev/null
sysctl net.netfilter.nf_conntrack_max 2>/dev/null
ulimit -n
cat /proc/sys/fs/file-nr
检查某个服务进程的文件描述符限制:
cat /proc/12345/limits | grep -i "open files"
ls /proc/12345/fd | wc -l
这里的 12345 需要替换为实际PID。/proc/进程号/limits 反映的是该进程自身限制,不能用当前登录Shell的 ulimit -n 代替。
低风险验证与参数变更边界
如果证据明确指向某一项参数,可以先做临时、单项、可回退的验证。变更前应记录旧值、确认适用内核和服务模型,并评估影响范围。不要同时修改多个参数,否则无法判断哪个变化产生了效果。
例如只读取并临时验证监听队列上限:
old_somaxconn="$(sysctl -n net.core.somaxconn)"
echo "old net.core.somaxconn=${old_somaxconn}"
sudo sysctl -w net.core.somaxconn=2048
sysctl net.core.somaxconn
上面的数值只是示例,不是所有系统都适合使用的固定值。若验证没有改善,或出现内存、延迟、连接排队等副作用,应恢复原值:
sudo sysctl -w net.core.somaxconn="${old_somaxconn}"
sysctl net.core.somaxconn
如果验证有效,也不要立即把临时值写入全局配置。应先确认:
- 应用是否确实设置了相匹配的监听队列;
- 连接排队是否下降;
- 应用accept速度是否足够;
- CPU、内存和请求延迟是否没有恶化;
- 重启后是否需要持久化;
- 该参数是否受发行版、内核版本或服务管理方式影响。
需要持久化时,先备份现有配置,采用单独文件并记录变更原因、旧值、新值和回滚命令。修改系统配置属于高影响操作,应安排维护窗口或至少准备服务级回退方案。不要在没有备份的情况下直接覆盖系统配置,也不要使用 sysctl -p 盲目加载一整套未经验证的参数。
如何判断应用修复优先级
如果CPU 100%告警具有以下特征,应用修复通常比内核调优更重要:
- 单个应用线程或进程稳定占用CPU;
- CPU高峰与某个接口、任务或批处理同时发生;
- 请求量没有明显增加,但单请求耗时和CPU成本升高;
- 应用日志出现重试、异常循环或超时堆积;
- 数据库慢查询或执行计划显示扫描、排序、聚合成本明显;
- 调大队列、缓冲或文件描述符后,CPU仍然持续升高。
可以用“每个请求消耗多少CPU时间”作为辅助指标。假设某服务在每秒处理1000个请求时占用约4个逻辑核心,那么平均每个请求大约消耗:
4个核心 × 1秒 ÷ 1000个请求 ≈ 0.004个CPU秒/请求
如果请求量仍是每秒1000个,但CPU增加到8个核心,说明单请求CPU成本大致翻倍;此时更应寻找代码路径、数据量、序列化、压缩、查询或重试变化,而不是先提高系统上限。

对于数据库,也可以按同样思路比较单位查询成本。查询数量、扫描行数、平均耗时和CPU时间同时上升时,优化SQL或访问模式通常比增加连接数更有价值。
如何判断内核调优确实有必要
内核参数调优更适合以下场景:
- 监听队列有明确溢出,而应用处理能力和上游请求速率合理;
- 进程因文件描述符限制被拒绝,但确认不存在连接泄漏;
- 连接跟踪表确实达到上限,且扩大容量后的内存成本可接受;
- socket缓冲区被明确证明限制了吞吐,而不是网络链路、应用读取速度或丢包导致;
- 内存和磁盘容量能够承担参数增大后的额外开销;
- 参数变更能解释告警中的具体现象,并且有可观察的验证指标。
例如,监听队列问题的完整判断不能只有“连接数很多”。至少应同时确认监听端口、队列状态、连接建立失败或超时、应用accept速度和CPU状态。如果CPU user已经很高,说明应用处理不过来,此时单纯增大队列可能只会让更多请求在内核中等待。
文件描述符问题也类似。查看到打开文件数接近上限,只能说明限制可能存在,还要排除连接泄漏、日志文件未关闭、客户端连接未复用等应用问题。提高 LimitNOFILE 后,必须继续观察进程打开文件数是否持续增长。
一个可执行的排查顺序
可以按照以下顺序处理反复出现的告警,每一步都根据结果决定是否进入下一层:

- 保存现场:记录时间、持续时长、业务影响、部署和定时任务;采集
uptime、mpstat、vmstat、pidstat。 - 拆分CPU状态:确认是user、system、iowait、softirq、irq还是steal占主导。
- 定位进程和线程:用
ps、top -H、pidstat -t确认是否存在单一热点。 - 排除资源等待:查看内存、交换、磁盘延迟、I/O队列、网络连接和软中断。
- 进入应用链路:把热点线程关联到接口、任务、异常日志、请求量和版本变更。
- 进入数据库链路:核对活动查询、慢查询、锁等待、连接数和执行计划。
- 核对内核限制:只针对已经出现的队列溢出、描述符耗尽、连接跟踪耗尽等证据检查参数。
- 单项验证:一次只改一个参数,记录旧值,限定影响范围,并观察业务指标。
- 决定长期动作:应用热点进入代码或SQL修复;资源边界问题才形成参数变更;虚拟化争用则转向宿主机资源处理。
修复后不能只看告警是否消失
一次CPU告警暂时恢复,不等于问题已经解决。修复或调参后,至少应在相近请求量、相近数据量和相近时间窗口内对比以下指标:
| 对比项 | 需要确认的结果 |
|---|---|
| CPU user/system/iowait/steal | 原来的主导异常是否消失,是否转移成新的瓶颈 |
| 单进程和单线程CPU | 热点线程是否下降,是否只是热点转移 |
| 请求量和响应时间 | 吞吐是否保持,P95或P99延迟是否改善 |
| 错误率和超时 | 队列变大后是否出现更多超时 |
| 内存和交换 | 调大缓冲、连接表后是否产生额外内存压力 |
| 磁盘延迟和队列 | 日志、数据库或回写是否变慢 |
| 网络连接与重传 | 连接堆积、重传或短连接是否减少 |
| 数据库查询指标 | 慢查询、扫描行数、锁等待和连接数是否改善 |
可以设置一个与业务相关的观察窗口,而不是只观察几分钟。若问题每天固定发生,应至少覆盖一次原有触发时段;若由发布或流量触发,则应在相近负载下比较。参数变更后CPU下降但请求延迟、错误率或内存占用上升,不能判定为成功。
当结果出现不同方向时,选择路径可以直接归纳为:
- user CPU和热点线程高:修应用、任务、查询或数据处理逻辑。
- system、softirq或irq高:查系统调用、网络包、连接模式和I/O路径,再考虑针对性参数。
- iowait高:查磁盘、数据库I/O和内存回收,不要优先调CPU参数。
- steal高:核查虚拟化资源争用,虚拟机内调参通常不是根本动作。
- 明确存在队列、描述符或连接跟踪耗尽:在记录旧值、评估内存和准备回滚的前提下,进行单项内核参数验证。
- 没有明确资源边界证据:不要为了让告警暂时下降而批量调参,继续沿进程、请求和数据库链路寻找实际消耗来源。