服务器CPU长期居高不下排查思路

25次阅读
没有评论

前言:CPU飙升不可怕,可怕的是没有排查思路

服务器CPU长期居高不下,是运维和开发最常遇到的“疑难杂症”之一。很多人一看到CPU报警就慌了,要么盲目重启,要么凭感觉乱杀进程,结果问题反复出现。其实,只要掌握一套系统的排查思路,CPU高就能像“捉虫子”一样被精准定位并解决。本文将从现象分析、工具使用、常见原因到具体解决步骤,带你一步步拆解。

一、先别急着操作,看懂CPU指标再说

CPU使用率长期超过80%、90%,甚至100%?首先要明确这个“长期”是多久。如果是偶发尖峰,可能只是任务调度或定时任务触发;如果是持续高位,那才是真正的“长期居高不下”。我们要关注的是:用户态CPU使用率(us)、系统态CPU使用率(sy)、等待I/O(wa)、软中断(si)以及空闲率(id)。比如us高说明用户进程在疯狂计算;sy高说明内核调用频繁;wa高意味着磁盘IO在拖后腿。拿到top命令的输出,先看这一行。

二、核心排障工具与实战命令

工欲善其事,必先利其器。下面几个命令将贯穿整个排查过程:

  • top / htop:实时查看CPU占用最高的进程,按P键按CPU排序。
  • pidstat:按进程粒度显示CPU使用情况,可以历史采样。
  • perf:性能剖析神器,能告诉你程序到底在忙什么。
  • strace:追踪系统调用,适合用户态CPU高的场景。
  • vmstat / iostat:看系统整体资源,特别是CPU等待I/O情况。
  • ps:搭配—sort=-%cpu使用,筛选TOP N进程。

实际思路:先用top找到哪个进程在吃CPU,然后根据进程类型选择进一步工具。比如Java进程就用jstack看线程堆栈,Python进程就用py-spy,C/C++程序用perf top。

三、从用户态CPU高说开去

最常见的是用户态CPU高,即进程本身在大量运算。排查步骤:

  1. 定位进程top -c可以看到命令全路径,比如发现一个php-fpm进程CPU占500%。
  2. 查看进程的线程top -Hp [pid],找到具体线程ID。
  3. 获取线程堆栈:Java程序用jstack [pid] | grep -A30 [十六进制线程ID],C/C++用gdb attach或strace。
  4. 分析热点函数:比如使用perf top -p [pid],直接看到是哪个函数在消耗CPU。

常见原因:无限循环、死锁、大量计算(如正则回溯、加解密)、垃圾回收频繁(GC线程疯狂CPU)。比如Java的Full GC会导致CPU飙升,此时要dump堆内存分析。

四、系统态CPU高:内核调用频繁

如果top里sy(system)占比高,说明内核在帮进程做事。可能是:

  • 大量系统调用:比如频繁的IO读写、网络请求。可以用strace -c -p [pid]统计系统调用次数,看看哪个调用最多。
  • 锁竞争:自旋锁或互斥锁导致CPU空转。perf可以显示spin_lock等热点。
  • 中断处理:硬中断或软中断过高。查看/proc/interrupts/proc/softirqs

解决方法:如果是系统调用过多,考虑业务层合并请求、使用缓冲;如果是锁竞争,需要优化代码加锁粒度或使用无锁数据结构;如果是中断,检查网卡、磁盘硬件或者驱动问题。

五、等待I/O高:CPU在“等”而不是“算”

wa高但us和sy不高?CPU其实在空转等待磁盘或网络IO完成。这个时候用iostat -x 1看磁盘的%util、await、svctm。如果await远大于svctm,说明IO队列过长。常见原因:数据库慢查询、日志写入过多、SWAP频繁换页。优化方向:加索引、调整日志策略、增加内存减少SWAP、换SSD硬盘。

六、软中断与硬中断:底层干扰

如果si(软中断)占比较高,通常与网络或网卡有关。比如大量小包冲击导致NET_RX软中断处理不过来。可以用watch -d cat /proc/softirqs看哪个中断在增长。解决办法:调整RPS/RFS(Receive Packet Steering)、网卡多队列、或者升级网卡驱动。还有种情况是时钟中断(TIMER)导致,但一般不常见。

七、虚拟化与容器环境特殊点

在云服务器或Docker/K8s环境中,CPU高可能是“受害者”场景:同宿主机的其他容器抢CPU,或者宿主机的CPU steal(偷取)时间高。检查/proc/stat中的steal字段,或者用top看“%st”。如果st高,说明宿主机超分严重,需要迁移或提工单。另外容器内存限制导致频繁SWAP也会让CPU升高。

八、实战案例演练:一个Java服务的CPU排查

假设我们有一个Java应用CPU持续>90%。步骤如下:

  1. top找到PID=12345,CPU 200%。
  2. top -Hp 12345,看到线程12346占最高。
  3. printf ‘%x
    ‘ 12346 得到十六进制0x303a。
  4. jstack 12345 | grep -A50 “0x303a”,看到线程堆栈里全是“sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run”。
  5. 原来是一个RMI连接循环处理请求,但连接池泄漏导致不断创建新连接。perf top显示大量socket相关函数。
  6. 分析连接池配置,限制最大连接,修复代码。

九、监控与自动化:防患于未然

排查一次是治标,建立监控才是治本。推荐配置:

  • Prometheus + Grafana,收集CPU使用率、用户态/系统态/wa/steal等。
  • 设置告警阈值,比如us > 80%持续5分钟。
  • 自动抓取故障时段的top、perf、jstack快照。
  • 定期分析慢日志和性能报告。

这样下次CPU再高,你手头就有证据链,不用从头手工排查。

十、总结:黄金排查口诀

一看top找进程,二看用户还是sys;用户态高用perf,系统态高查调用;wa高务必看磁盘,软中断高查网络;虚化环境看steal,容器不要忘limit。 掌握这套思路,任何服务器CPU长期居高不下问题都能快速定位。别忘了排查后做好复盘,把经验转为自动化监控,才能从救火队员变成防火专家。

正文完
 0
评论(没有评论)