Linux 服务器 CPU 飙高但网站还能打开?定位 Java、PHP、Nginx 与内核占用

Linux 服务器 CPU 飙高但网站还能打开,通常说明服务未完全中断,但容量余量正在被消耗。本文按 top、vmstat、pidstat、jstack、PHP-FPM 慢日志和 Nginx 日志的顺序,拆解用户态、内核态、I/O 等待、软中断和 steal 的排查方法,并给出修复顺序与验证指标。
Linux 服务器 CPU 飙高但网站还能打开?定位 Java、PHP、Nginx 与内核占用

Linux 服务器 CPU 使用率飙到 80% 或 90% 以上,网站却还能打开,这种情况比直接宕机更容易误判。页面偶尔能返回,说明链路没有完全断开,但队列、响应时间和连接数可能已经在恶化。本文按实际排查顺序,拆开 %us%sy%wa%si%st 的含义,并分别定位 Java、PHP-FPM、Nginx 和内核线程的占用来源。

排查思路是:先确认 CPU 高出现在哪个时间窗口和哪个逻辑核,再找到具体进程和线程,最后判断属于业务计算、锁等待、进程频繁创建、网络中断、虚拟化资源争抢还是内核回收。不要一看到 CPU 高就重启服务器,现场被清掉后往往只能等下一次故障。

一、先看 top 的 CPU 行

登录服务器后先用 top 或 vmstat 建立观察窗口:

top

vmstat 1

top 展开后的 CPU 行里,这些字段比总使用率更有信息量:

  • %us:用户态 CPU,通常来自 Java、PHP、Python、Node.js 等业务进程。
  • %sy:内核态 CPU,常见于频繁系统调用、进程创建、内存回收、锁竞争、网络包处理。
  • %wa:等待磁盘 I/O,不算真正消耗计算能力,但会让请求变慢。
  • %si:软中断,常见于高网络包速率、小包过多或网卡队列分布不均。
  • %st:虚拟化环境被宿主机抢走的时间,云服务器上要特别留意。

网站还能打开但 %us 很高,优先查业务进程。%sy 高则看系统调用、进程、线程和内存活动。%si 高看网络包和网卡队列。%st 高说明瓶颈可能不在虚拟机内部,需要检查云监控、实例规格和宿主机争抢。

top 里按数字 1 可以展开每个逻辑核。如果只有一个核跑满,其他核空闲,常见原因是单进程或单线程瓶颈、中断集中在某个核、绑核配置不合理。所有核同时高,则更像是请求量、扫描、重计算或整体容量不足。

二、确定是哪个进程

先用 top 按资源排序,再配合 pidstat 做连续采样:

top -H -d 2

pidstat -u 2 5

如果系统没有 pidstat,Debian 或 Ubuntu 可以安装 sysstat,CentOS Stream、Rocky Linux、AlmaLinux 同样通常由 sysstat 软件包提供。安装前遵守公司或云服务器的变更规则。

找到可疑进程后,把 PID 交给 /proc 和命令行确认:

readlink /proc//exe

tr '\0' ' ' < /proc//cmdline

ps -eo pid,ppid,user,stat,pcpu,pmem,etime,cmd –sort=-pcpu | head -30

exe 能确认二进制路径,cmdline 能看到启动参数。两者与预期不一致时,不要直接杀进程,先确认是否属于计划任务、监控 Agent、备份任务或安全软件。

三、Java 进程:从线程栈找热点

Java 服务经常表现为 %us 高、网站仍能响应但接口延迟明显。先用 top 找到 Java 线程号,再把线程号转成十六进制:

printf '%x\n'

jstack | grep -A 20 -i ''

jstack 输出里重点看 RUNNABLE 线程在做什么:JSON 序列化、正则匹配、加密解密、大集合遍历、GC 线程高频运行,都会让用户态 CPU 升高。如果线程大量处于 BLOCKED,CPU 可能不高但延迟很高;此时要查锁持有者,而不是盲目加 CPU。

再结合 GC 日志和 JVM 统计:

jstat -gcutil 1000 10

Young GC 或 Full GC 频繁、Old 区回收后占用仍然很高,CPU 高可能是内存分配压力的表现。此时扩容 CPU 只能缓解表象,真正要处理的是对象分配、缓存无界增长、堆设置和查询量。

Java 16+ 提供 jfr 采样。生产环境采样要控制时长和频率,先使用短窗口采样,把影响控制在小范围内。容器里的 Java 进程还要确认可见 CPU 和内存限制,避免 JVM 按宿主机资源推断默认参数。

四、PHP-FPM:先看进程状态和慢日志

PHP 站点的高 CPU 常与请求阻塞叠加出现。先用进程管理接口确认 worker 状态:

curl -s http://127.0.0.1/status | head -50

重点看 active workers、idle workers、listen queue 和 max children reached。active 满且 listen queue 增长,说明请求已经排队;此时 CPU 高可能是 worker 全部忙,也可能是少数慢请求拖住了进程池。

再打开 PHP-FPM 慢日志。request_slowlog_timeout 通常设置成能捕获异常请求但不会把整个进程池打满的值,例如 3s 或 5s,具体要按接口耗时分布调整:

slowlog = /var/log/php-fpm/www-slow.log

request_slowlog_timeout = 3s

慢日志里的脚本路径和行号只是入口,还要继续查外部 HTTP 调用、数据库查询、图片处理和循环。pm.max_children 不是越大越好,超过内存和 CPU 承载能力后,站点会从排队变成重启。

五、Nginx:区分进程忙和连接堆积

Nginx 自身 CPU 高相对少见,更多时候是连接、TLS 握手、日志和代理转发带来的系统性开销。先看状态:

nginx -T | grep -E 'worker_processes|worker_connections|access_log|gzip'

ss -s

如果 access log 记录了大量扫描请求、TLS 握手和小文件请求,CPU 可能被低价值流量消耗。gzip 对文本压缩会带来额外 CPU,静态大文件适合前置到 CDN 或对象存储,而不是每次都由源站压缩。

高并发下可以检查 worker 数与逻辑核数量是否匹配。通常 auto 是合理默认值,绑核和优化要结合实际负载做,不要照抄模板。开启 $request_time$upstream_response_time$upstream_addr 记录,能区分 Nginx 自身忙还是后端慢。

六、内核和系统线程的 CPU 占用

%sy%si 高时,top 里可能出现 ksoftirqdkworkerkswapd 等内核线程。它们不是独立故障源,而是某种内核活动的表现。

ksoftirqd 高通常看网络中断分布:

cat /proc/interrupts

ss -s

如果网卡队列集中在一个逻辑核,高流量时会造成单核跑满。云服务器上能调整的范围有限,可以从减少小包、限制扫描流量、开启缓存、检查负载均衡转发模式入手。

kworker 高可以结合 I/O 和内存活动看:

vmstat 1

iostat -x 1

常见诱因是磁盘压力大、频繁写日志、备份任务碰上流量高峰。kswapd 高则说明内存紧张,系统在频繁回收页面。直接内存回收还会推高 %sy,此时重点查内存和无缓存命中率,而不是继续加 CPU。

七、把外部依赖也纳入排查

应用进程 CPU 高,不一定是代码计算多。数据库慢查询、外部 API 慢、连接池耗尽、反复重连,都会让业务线程忙等、序列化和重试增加。至少把这些日志放到同一个时间窗口:

  • Nginx access log 的状态码、请求耗时、上游耗时。
  • 应用错误日志和结构化日志。
  • 数据库慢查询日志或云数据库监控。
  • CDN 回源监控和云服务器网络监控。

如果 CPU 升高时间点与定时备份、日志压缩、病毒扫描、发布上线重合,优先检查计划任务。可以用下面的命令看近期任务:

grep 'cron' /var/log/syslog | tail -100

systemctl list-timers –all

八、修复时要避免的做法

这几类操作容易把问题放大:

  • CPU 高就重启服务器,丢失进程队列、线程栈和日志现场。
  • 只看平均 CPU,不看单核分布和 %sy%wa%st
  • 无容量依据地调大 worker、线程池或连接池。
  • 关闭安全软件、监控 Agent 或内核机制来“降 CPU”。
  • 在生产高峰期做大规模配置试验。

修复顺序应该是:先限流或摘掉异常流量,保留日志;再定位到进程和线程;确认是应用、数据库、内核还是资源限制;最后扩容或优化。每次只改一个变量,并记录变更时间,方便后续用监控对齐。

九、验证恢复

处理完成后,不要只看一次 top。至少确认五个指标:

  • CPU 使用率回到正常基线,单核不再长期 100%。
  • %wa%si%st 没有新的异常。
  • HTTP 状态码、5xx 比例和 P95 延迟恢复正常。
  • 应用队列、连接池、数据库活跃连接回落。
  • 日志中没有持续增加的错误、重启或 OOM 记录。

观察时间要覆盖一个流量高峰或一个定时任务周期。CPU 飙高如果只在凌晨备份、定时发布或爬虫集中访问时出现,短时间采样会漏掉根因。

结论

CPU 飙高但网站还能打开,说明系统仍在服务,但容量余量已经被吃掉。先用 top、vmstat 和 pidstat 分清用户态、内核态、I/O 等待、软中断和 steal,再定位到 Java 线程、PHP-FPM worker、Nginx 请求或内核活动,最后结合 Nginx、应用、数据库和云监控的时间线判断根因。保留现场、单变量调整、覆盖高峰验证,比盲目重启更容易把问题真正收掉。

参考资料

Linux man-pages:top(1)
https://man7.org/linux/man-pages/man1/top.1.html

Linux man-pages:vmstat(8)
https://man7.org/linux/man-pages/man8/vmstat.8.html

sysstat 文档:pidstat(1)
https://man7.org/linux/man-pages/man1/pidstat.1.html

OpenJDK 文档:jstack 命令
https://docs.oracle.com/en/java/javase/21/tools/jstack.html

OpenJDK 文档:jstat 命令
https://docs.oracle.com/en/java/javase/21/tools/jstat.html

PHP-FPM 配置:request_slowlog_timeout
https://www.php.net/manual/en/install.fpm.configuration.php

NGINX 文档:Core functionality
https://nginx.org/en/docs/ngx_core_module.html

实操指南知识库

网站一直 502/504?CDN、Nginx 与源站超时排查指南

2026-9-14 10:48:26

知识库

容器死而不僵?揭开 Zombie 容器堆积背后的运维陷阱与治理方法

2025-7-1 10:36:54