上周在维护一台 2 核 4G 的轻量云服务器,宝塔后台频繁报警 CPU 飙到 100%,站点偶尔直接返回 502 Bad Gateway。ssh 登录终端一敲 top,发现视线所及全都是 php-fpm: pool www 进程在疯狂吃算力。顺手把当时的排查排错步骤,以及后来跑得比较稳的参数记录下来,方便以后查阅,也给遇到类似情况的朋友一个参考。
1. 临时恢复与初步观察
当线上业务已经卡死无法访问时,第一件事是先让服务恢复响应,再留存现场做排查:
bash# 临时平滑重载 PHP 服务(根据自己的 PHP 版本调整,如 php-fpm-74) systemctl reload php-fpm-74
重启后 CPU 会瞬间回落,此时需要盯住 top 或 htop,观察进程是缓慢上升还是瞬间拉满:
- 如果刚启动就瞬间涌入几十个 php-fpm 进程把 CPU 打满,往往是瞬时并发请求过大,或者遭受了爬虫扫盘;
- 如果只有 1-2 个进程长期霸占单核 100% 算力不释放,通常是某个 PHP 脚本陷入了死循环或复杂计算。
2. 关键参数设置与内存匹配
宝塔默认安装完 PHP 后,运行模式通常采用 dynamic(动态)。很多站长在网站卡顿后习惯性盲目调大 max_children,但这往往是导致 CPU 和内存雪崩的根源。
一个跑着 WordPress 或 Typecho 的 PHP-FPM 子进程,通常会占用 30MB 到 60MB 内存。如果内存耗尽触发了系统 Swap 频繁换页,CPU 的 iowait 会急剧飙升,表现出来也是 CPU 跑满。
针对不同规格的服务器,可以参考以下这套偏向保守稳定的参数搭配:
| 服务器配置 | 建议 max_children | start_servers | min_spare_servers | max_spare_servers | 建议 Swap 大小 |
|---|---|---|---|---|---|
| 1核 1G | 10 | 2 | 2 | 5 | 1024MB |
| 2核 2G | 20 | 3 | 3 | 10 | 2048MB |
| 2核 4G | 40 | 5 | 5 | 20 | 2048MB |
| 4核 8G | 80 | 10 | 10 | 40 | 4096MB |
在宝塔面板的【PHP 管理】->【性能调整】中即可修改这些数值。此外,一定要设置 max_requests(每个子进程处理多少请求后自动重启释放内存),建议填 500 到 1000,能有效防止 PHP 第三方扩展的内存泄漏。
3. 开启慢日志抓出具体卡顿代码
如果调整了并发参数后,CPU 依然偶尔被单进程跑死,大概率是程序里有执行过慢的代码(如远程 API 超时、慢 SQL 查询、未加索引)。
可以在 PHP 配置文件中开启慢日志定位真因:
ini; 记录超过 3 秒的请求 request_slowlog_timeout = 3s slowlog = /www/server/php/74/var/log/slow.log
修改后重载 PHP 配置。当再次出现 CPU 飙高时,直接查看日志:
bashtail -n 50 /www/server/php/74/var/log/slow.log
慢日志会精准打印出具体是哪个文件、第几行代码执行了多久,顺藤摸瓜去修补插件或优化 SQL 语句即可。
4. 为什么改完参数后还是会被打崩?
在实际排查中,有超过一半的 CPU 跑满案例不是代码问题,而是网站正在被垃圾爬虫高频抓取。动态内容每次请求都要穿透到 PHP 解析,几个并发爬虫就能把双核服务器拉垮。
此时可以实时查看 Nginx 访问日志:
bashtail -f /www/wwwlogs/你的域名.log
排查要点:
- 检查 User-Agent:很多没有标注良性爬虫标签的 Python 脚本、Go-http-client、SemrushBot 等,可以直接在 Nginx 配置中通过规则直接
return 403。 - 开启 OPcache 脚本缓存:在宝塔 PHP 安装扩展处勾选
opcache,能把编译后的字节码常驻内存,大幅减轻 PHP-FPM 重复编译解析的 CPU 开销。 - 动静分离与页面级缓存:对于博客类站点,配合 Redis 缓存插件直接输出 HTML 页面,请求不用每次都走完整的 PHP 生命周期,CPU 占用通常能直降 70% 以上。