宝塔面板 PHP-FPM 进程 CPU 飙升排查与配置记录

核心导读:记录一次轻量云服务器上 PHP-FPM 进程跑满 CPU 的真实排查过程,包含进程调优参数参考、慢日志定位瓶颈代码以及非配置原因的防范思路。

上周手头一台 2G 内存的云服务器频繁告警,CPU 连续十几分钟维持在 95% 以上,前端页面直接响应超时报 502 Bad Gateway。连上终端用 top 一看,四五个 php-fpm: pool www 进程死死霸占了 CPU。

这种问题在跑 WordPress、Typecho 或一些 PHP 框架的轻量机器上很常见。很多人遇到后第一反应是重启 PHP 甚至重启服务器,但通常治标不治本,几十分钟后又会重演。这里把这次排查的思路和目前调教得比较稳的参数记录下来,留个备忘。

1. 临时恢复与现场定位

如果站点已经无法访问,最快恢复服务的方式是在宝塔面板的「软件商店」-「运行环境」里找到对应的 PHP 版本,点击「重启」。或者在终端执行命令:

bash
systemctl restart php-fpm-74 # 根据实际版本号调整,如 php-fpm-80

但在重启之前,如果还能敲命令,建议先用以下命令确认到底是哪个脚本把进程卡死了:

bash
# 查看占用 CPU 最高的 5 个进程及具体执行路径 top -c -b -n 1 | grep php-fpm | head -n 5

同时检查当前 PHP-FPM 的活动进程数:

bash
ps -ef | grep 'php-fpm: pool' | grep -v grep | wc -l

如果进程数已经达到了配置的 max_children 上限,后续新的 HTTP 请求就只能在队列里排队,队列满了 Nginx 就会直接给客户端吐 502。

2. 关键参数设置参考

很多小内存机器(1G/2G)跑崩,是因为使用了宝塔预设的较高进程数配置。单个 PHP-FPM 进程在处理复杂脚本时,通常会吃掉 30MB 到 60MB 内存。如果进程数设得太大,内存一旦见底,系统会疯狂使用 Swap 交换分区,导致磁盘 IO 飙升、CPU 处于等待状态,最终全部卡死。

在宝塔面板打开对应的 PHP 设置,切换到「性能调整」,以下是我在不同规格机器上长期实测比较稳妥的配置:

服务器配置运行模式max_children (最大子进程)start_servers (启动进程)min_spare_servers (最小空闲)max_spare_servers (最大空闲)建议 Swap 大小
1核 1Gondemand / dynamic5 - 82251024 MB
2核 2Gdynamic15 - 2055152048 MB
2核 4Gdynamic30 - 401010302048 MB
4核 8Gdynamic60 - 801515504096 MB

对于 1G 内存的超小机器,建议将运行模式改为 ondemand(按需分配),有请求时才生成进程,空闲自动释放,能极大降低待机内存开销。

3. 开启慢日志揪出耗时代码

调整进程数量只能保证服务器不被挤爆,但如果 CPU 依然居高不下,核心原因往往是某个 PHP 脚本本身执行极慢(比如死循环、复杂的正则表达式、没加索引的慢数据库查询,或者调用了外部超时的第三方 API)。

抓出这些脚本最有效的工具就是 PHP-FPM 的慢日志(Slowlog)。进入宝塔 PHP 设置里的「配置文件」,找到并修改以下两行(如果被分号注释掉了,把分号删掉):

ini
request_slowlog_timeout = 3 slowlog = /www/server/php/74/var/log/slow.log

request_slowlog_timeout = 3 意味着只要有请求执行时间超过 3 秒,PHP-FPM 就会把完整的调用栈记录下来。修改后在面板点击「重载配置」。

等网站再次出现卡顿或者运行一段时间后,去查看 /www/server/php/74/var/log/slow.log

bash
tail -n 50 /www/server/php/74/var/log/slow.log

日志里会精确显示脚本执行到了哪一个文件、哪一行代码、调用了哪个函数。根据堆栈信息,基本一眼就能看出来是哪个插件在拖后腿,还是某条 SQL 查询耗光了资源。

4. 调整完依然卡死的原因排查

如果代码本身没有大问题,但 PHP-FPM 偶尔还是会瞬间被拉爆,通常要排查以下外部因素:

  1. 恶意爬虫与扫描行为:大量劣质爬虫高频并发抓取动态页面,或者有扫描器在死刷 /wp-login.php/xmlrpc.php 等接口。每一个请求都会启动一个 PHP 解释器,瞬间把 CPU 占满。可以在 Nginx 访问日志中过滤出请求频次最高的 IP,配合 Nginx 的 limit_req 模块限流,或者开启面板里的 Nginx WAF 防火墙模块拦截这类无用扫描。
  2. 外部接口调用挂起:脚本中使用了 file_get_contents() 或未设超时的 curl 去请求外部 API,一旦目标服务器响应缓慢,当前 PHP-FPM 进程就会一直挂起等待,直到达到 max_execution_time。排查代码时务必为所有远程请求设置 3-5 秒的硬超时。
  3. 数据库锁表:MySQL 中有长时间未完成的事务或者写入锁,导致后续 PHP 进程全都在等待数据库返回结果。排查 PHP 问题的同时,最好也检查一下 MySQL 的 slow_query_log 或使用 SHOW FULL PROCESSLIST; 查看是否有阻塞语句。

经过这轮调整,把子进程上限控制在内存允许范围内,并根据慢日志修复了两个超时调用,这台 2G 机器的 CPU 占用基本回落到了日常 15% 左右,没有再出现无故死机的情况。

常见问题与解答 (FAQ)

Q 修改了 PHP-FPM 的性能参数后需要重启服务吗?

需要。在宝塔面板的 PHP 设置界面点击「重载配置」或「重启」才会让新参数生效,通常点击平滑重载即可,不会中断现有访客。

Q 小内存机器设置了 Swap 虚拟内存,会不会影响磁盘性能?

虽然 Swap 的读写速度远低于物理内存,但它的主要作用是在突发高负载时充当缓冲垫,防止 PHP 进程因物理内存耗尽直接被系统 OOM 强行杀掉而报 502。轻微的性能代价换取系统可用性是完全值得的。

返回运维知识库