最近在看监控看板时,发现手头一台 2G 内存的轻量云服务器隔三差五 CPU 就会飙到 100%,进终端一敲 top,清一色全是 php-fpm: pool www 的子进程在死命吃资源,网站偶尔还会跳出 502 Bad Gateway 报错。
这类问题平时很常见,大部分是进程管理模式没配对、恶意爬虫高频扫目录,或者是某个插件里有死循环慢查询。趁着把机器跑平稳了,顺手把排查思路和目前在跑的配置记下来备忘。
1. 紧急处理与即时定位
如果站点已经卡死打不开了,第一步肯定是先恢复访问:
在宝塔面板后台找到对应的 PHP 版本(如 PHP 7.4 或 8.1),点击“服务”选择“重载配置”或“重启”。如果后台卡得进不去,直接 SSH 登录服务器执行系统命令:
bashsystemctl restart php-fpm-74
恢复基本访问后,立刻在终端用命令行查看当前吃 CPU 最高的是哪几个进程:
bashtop -c
按大写 P 可以按 CPU 使用率排序,看具体是哪个站点的 pool 或者是哪条命令在跑。如果发现很多请求停留在同一个脚本上,说明基本就是特定页面被疯狂请求或者代码卡住了。
2. 关键运行参数与内存配置建议
宝塔默认安装 PHP 后的并发设置通常比较激进,对 1G-2G 的小机器不太友好。PHP-FPM 默认模式一般是 dynamic(动态)或 ondemand(按需),如果 max_children 设得太大,一个子进程占用 30MB-60MB 内存,多个进程并发跑脚本时不仅会把内存吃干,还会频繁引发系统上下文切换,导致 CPU 瞬间占满。
进入宝塔「PHP 管理」->「性能调整」,建议根据服务器物理内存合理调低进程数:
| 机器内存 | 运行模式 | max_children (最大进程) | start_servers (启动进程) | min_spare (最小空闲) | max_spare (最大空闲) | Swap 建议配置 |
|---|---|---|---|---|---|---|
| 1 核 1G | ondemand / dynamic | 10 - 15 | 2 | 2 | 5 | 1024 MB |
| 2 核 2G | dynamic | 20 - 30 | 5 | 5 | 15 | 2048 MB |
| 2 核 4G | dynamic | 40 - 60 | 10 | 10 | 30 | 2048 MB |
除了调小并发,还有两项配置很管用:
- max_requests:默认常常是 0(不限制),建议修改为
500到1024。这样每个子进程处理完指定数量的请求后就会自动重启销毁,能有效防止某些代码编写不当造成的内存泄漏。 - 添加 Swap 虚拟内存:如果服务器本身没做 Swap,内存一旦吃紧,系统就会频繁触发 OOM 或者拼命换页导致 CPU 飙升。可以在宝塔的“Linux工具箱”里直接挂载与物理内存等大的 Swap,或者在终端手动加一个 swapfile。
3. 开启慢日志抓取异常脚本
光改并发参数只是治标,如果网站里有一段 SQL 查了几十万行数据,或者调用了外部卡死超时的 API,即使只开 5 个进程,也一样能把 CPU 占到 100%。
排查这类隐患最有效的办法是开启 PHP-FPM 的慢日志(Slow Log):
打开宝塔对应的 PHP 配置文件(php-fpm.conf),检查或添加以下配置:
inirequest_slowlog_timeout = 3s slowlog = /www/server/php/74/var/log/slow.log
保存后重载 PHP 配置。这段配置的意思是:一旦有请求处理超过 3 秒,PHP 就会把调用栈信息连同文件名、行号详细记录到 slow.log 中。
过一段时间查看日志文件:
bashtail -n 50 /www/server/php/74/var/log/slow.log
顺着日志里打印出来的函数调用栈,通常能直接定位到具体是哪个 WordPress 插件、哪个主题文件或者哪行数据库查询在死耗资源。
4. 为什么改完参数后还是会被打崩?
有些时候代码本身没有复杂逻辑,改完参数之后 CPU 还是时不时拉满,这多数是外部流量冲击造成的,主要分两类:
- 垃圾爬虫高频扫站:很多未经认证的野爬虫、漏洞扫描器会无节制地并发抓取动态页面(如搜索结果页、tag 聚合页),导致 PHP-FPM 进程池瞬间被打满。可以去 Nginx 访问日志里过滤一下高频访问的 User-Agent 或 IP。如果不想天天手工 grep 分析访问日志,也可以配合 Nginx 自身的 limit_req 模块做限流,或者开启面板里的安全防护插件、接入 CDN 过滤恶意请求。
- 静态文件被交给 PHP 解析:检查 Nginx 伪静态和配置文件,确认
css、js、图片等静态资源是由 Nginx 直接返回,而不是被兜底规则误传给了 PHP-FPM 处理。动态解释器去扛静态并发,属于典型的配置失误。