这几天在维护一台 2G 内存的云服务器时,监控频频发来报警,看后台发现几个 php-fpm 进程常年把单核 CPU 吃满,偶尔还会连带出现 Nginx 502 Bad Gateway 报错。
PHP-FPM 占用 CPU 偏高通常就两个大方向:要么是并发请求上来了,进程配置不合理导致系统在频繁创建/销毁进程;要么是代码里有死循环、慢查询,把单个进程长时间卡在执行状态。把这次的定位步骤和目前比较稳的配置随手记下来,省得以后换机器又得重头翻文档。
1. 临时应急与现场观察
登录服务器后第一件事不要盲目重启,先用终端连上去看看实际开销:
bashtop -c
输入大写 P 让进程按 CPU 排序。如果看到好几个类似 php-fpm: pool www 的子进程 CPU 占用都在 90% 以上,记下对应的 PID。
如果站点已经卡死,可以在宝塔面板的【软件商店】->【已安装】-> 找到对应的 PHP 版本点击【重启】,或者命令行执行 systemctl restart php-fpm-74(根据实际版本修改)。但这只能救急,几分钟后请求一多大概率还会满载。
2. 关键参数设置参考
宝塔默认安装完 PHP 后,PHP-FPM 的进程管理模式(pm)通常是 dynamic。如果给的 max_children 设得太大,小内存机器在并发进来时会迅速耗尽物理内存,系统开始大量走 Swap,导致 CPU 的 iowait 和系统开销瞬间暴增。
在宝塔【PHP设置】->【性能调整】里,可以参考以下针对常规配置服务器的调整值:
| 服务器配置 | 建议运行模式 (pm) | max_children | start_servers | min_spare_servers | max_spare_servers | Swap 大小 |
|---|---|---|---|---|---|---|
| 1核 1G | ondemand / dynamic | 10 | 2 | 2 | 5 | 1024 MB |
| 2核 2G | dynamic | 20 | 5 | 5 | 15 | 2048 MB |
| 2核 4G | dynamic | 40 | 10 | 10 | 30 | 2048 MB |
| 4核 8G | dynamic / static | 80 | 20 | 20 | 60 | 不需要或 2G |
几个参数的简要解释:
- max_children:同时处理请求的最大子进程数。一个 PHP-FPM 进程平均占用 20MB~40MB 内存(如果有图片处理库可能会更高)。2G 内存扣除 MySQL 和系统常驻,留给 PHP 的一般在 800MB 左右,给到 20-25 是比较安全的上限。
- max_requests:强烈建议不要设为 0。可以设为 1024 或 2048。它的作用是让子进程处理完指定数量的请求后自动退出并释放内存,有效防止第三方扩展或代码内存泄漏导致的资源积压。
3. 查看慢日志确认具体脚本
参数改小只能防止机器被直接打死,如果依然有单个进程持续占满 CPU,必须找出到底是哪一行代码在卡死进程。
打开宝塔对应的 PHP 配置文件,检查或开启慢日志(Slowlog):
inirequest_slowlog_timeout = 3 slowlog = /www/server/php/74/var/log/slow.log
这里的 request_slowlog_timeout = 3 表示任何执行超过 3 秒的请求都会被记录到慢日志中。保存后在宝塔面板平滑重载一次 PHP。
等 CPU 再次飙升时,直接在终端 tail 慢日志:
bashtail -n 50 /www/server/php/74/var/log/slow.log
通常慢日志里会直接标出具体的 PHP 文件名以及卡住的行数。常见的坑点包括:
- WordPress 插件调用了外部不可达的 API 接口,没有设置超时时间;
- 数据库单表十几万行数据没有建索引,执行了全表扫描排序;
- 文件上传目录中被注入了死循环执行的后门脚本。
4. 为什么改完参数后还是偶尔会被打崩?
如果代码本身没有死循环,但某一时段 CPU 突然被打高,多半是遇到了扫描器或者流氓爬虫。
- 高频爬虫抓取动态搜索页:
/search、/?s= 等动态查询接口的高频请求。这种接口不走静态缓存,一秒来几个就能把数据库和 PHP-FPM 全拖慢。这种情况建议在 Nginx 层通过 limit_req 模块限制单 IP 访问频率,或者直接在 Nginx 配置里屏蔽常见恶意 User-Agent(如 Scrapy、SemrushBot 等)。
- 检查 Opcache 是否开启:
opcache 处于已安装状态。开启它可以避免每次请求都重复编译 PHP 文件,能直接砍掉 30% 到 50% 的脚本 CPU 消耗。
- 利用日志分析插件辅助观察: