搜索全站
报错排查 8 分钟阅读

Debian 12 部署 WordPress 与 PHP-FPM 进程数调优:按内存算 pm.max_children

在 Debian 12 上部署 WordPress 后,PHP-FPM 进程数决定了站点能扛多少并发。本文讲清 pm.max_children 的计算方法、三种进程管理模式的取舍、慢日志定位技巧,以及 502、504、CPU 跑满等问题的对应调参方案,附可直接使用的配置模板。

U
UFO_PF 2022-07-24 · 阅读 8 分钟
De 报错排查
阅读提示
本文约 3,083 字,预计阅读 8 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:网站一有流量就 502,多半是 PHP-FPM 配置没算过

很多站点平时好好的,一做活动或者被爬虫扫一轮就开始 502、504。重启服务又好一会儿。去看日志,常见这么一句:

WARNING: [pool www] server reached pm.max_children setting (5), consider raising it

意思是:PHP 进程池满了,新请求排队,排到超时就被 Nginx 断掉。默认值(通常是 5)是按”能跑”设的,不是按”能撑流量”设的。

原理分析:PHP-FPM 进程池是怎么工作的

PHP-FPM 预先 fork 出一批 worker 进程,Nginx 通过 Unix socket 或 TCP 把请求交给空闲 worker。三种管理模式:

模式 行为 适用
static 固定数量的 worker 常驻 内存充足、流量稳定,性能最好
dynamic 在 min/max 之间按需扩缩 大多数站点的默认选择
ondemand 有请求才 fork,空闲后回收 内存极小、访问稀疏的站点

ondemand 看似省内存,但每次 fork 有开销,突发流量下响应会变慢,WordPress 场景一般不推荐。

关键参数:

pm.max_children      ; 同时最多多少个 PHP 进程
pm.start_servers     ; 启动时的进程数
pm.min_spare_servers ; 最少空闲进程
pm.max_spare_servers ; 最多空闲进程
pm.max_requests      ; 单个进程处理多少请求后重启(防内存泄漏)

保姆级实操

第一步:安装环境(Debian 12)

sudo apt update
sudo apt install -y nginx mariadb-server php8.2-fpm php8.2-mysql php8.2-curl \
  php8.2-gd php8.2-mbstring php8.2-xml php8.2-zip php8.2-intl php8.2-imagick

Debian 12 官方源即为 PHP 8.2,无需加第三方源,这是它相比 Ubuntu 22.04 的一个便利之处。

第二步:测算单个 PHP 进程占用

先让站点跑起来,访问几个页面,然后看实际占用:

ps --no-headers -o "rss,cmd" -C php-fpm8.2 | awk '{ sum+=$1 } END { printf "平均占用: %.1f MB\n", sum/NR/1024 }'

假设输出 45 MB。注意要取访问过几个典型页面之后的值,刚启动时进程很轻,不具参考性。

第三步:计算 pm.max_children

公式:

max_children = 可用于 PHP 的内存 / 单进程平均占用

比如服务器 4GB 内存,系统 + Nginx + MySQL 约占 1.2GB,留给 PHP 约 2.5GB:

2560 MB / 45 MB ≈ 56

保守取 40~50。不要按峰值内存算,因为 WordPress 后台操作(备份、导入)会临时用更多内存。

第四步:写配置

/etc/php/8.2/fpm/pool.d/www.conf:

[www]
user = www-data
group = www-data
listen = /run/php/php8.2-fpm.sock
listen.owner = www-data
listen.group = www-data

pm = dynamic
pm.max_children = 45
pm.start_servers = 8
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 500

request_terminate_timeout = 300s
slowlog = /var/log/php8.2-fpm.slow.log
request_slowlog_timeout = 5s

php_admin_value[memory_limit] = 256M

start_servers 一般设为 max_children 的 15%~20%,min/max_spare 覆盖日常波动区间。

第五步:开启慢日志定位问题页面

request_slowlog_timeout = 5s 会把执行超过 5 秒的请求记录下来,包括具体是哪个 PHP 文件的哪一行:

tail -f /var/log/php8.2-fpm.slow.log

典型输出:

script_filename = /var/www/mimangfei.com/index.php
[0x00007f...] mysql_query() /var/www/.../wp-includes/wp-db.php:1234

这种就是数据库慢查询,要去查插件或加索引。

第六步:同步调整 Nginx 与 PHP 超时

location ~ \.php$ {
    fastcgi_read_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_connect_timeout 60;
}

Nginx 的超时必须 ≥ PHP 的 request_terminate_timeout,否则会出现”PHP 还在跑,Nginx 已经返回 504″的怪现象。

第七步:验证

sudo systemctl restart php8.2-fpm
sudo tail -f /var/log/php8.2-fpm.log   # 不应再有 max_children 告警

简单压测(谨慎使用):

ab -n 200 -c 20 https://mimangfei.com/

常见踩坑与失败案例

  1. 把 max_children 设得很大导致 OOM —— 内存耗尽后系统会杀掉 MySQL,比 502 更惨。一定要留 30% 余量给系统和其他服务。
  2. 改了 www.conf 不生效 —— 必须 systemctl restart php8.2-fpm,reload 有时不重新读进程管理参数。
  3. sock 权限问题导致 502 —— listen.owner / listen.group 要与 Nginx 运行用户一致。
  4. 案例:某站点 2GB 内存却设了 pm.max_children = 100,流量高峰直接 OOM,MySQL 被 kill,数据库文件损坏。正确做法是降到 30 并加 Swap 缓冲。
  5. pm.max_requests 设 0 —— 表示永不重启进程,长期运行后内存泄漏会累积,建议设 500~1000。

总结

PHP-FPM 调优的核心就一句话:用实测内存反推进程数,而不是拍脑袋。流程是:先测单进程占用 → 按可用内存算 max_children → 配 dynamic 三件套 → 开慢日志观察 → 定期复盘。做完这一步,再配合页面缓存和 OPcache,一台 2 核 4G 的机器扛住日几千 IP 的 WordPress 站点是很轻松的。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。