PHP-FPM pm.max_children 参数最佳值需通过“系统可用内存除以单个 PHP 进程平均内存”计算,适用于独立 Web 服务器场景,风险边界是设置过高会触发系统 OOM 导致服务中断。
先说结论:严禁直接套用网络推荐值,必须基于当前业务进程的实际内存占用动态计算。
- 适合:独立部署 PHP-FPM 且内存资源有限的服务器
- 先准备:获取当前 PHP 进程平均 RSS 内存值和系统剩余可用内存
- 验收:观察系统负载、PHP 日志及是否有 OOM 记录
命令速用版
使用以下命令计算当前 PHP-FPM 进程的平均内存占用(单位 KB),用于后续计算:
ps aux | grep php-fpm | grep -v grep | awk '{print $6}' | awk '{sum+=$1} END {print sum/NR}'
查看系统总内存命令:
free -m
为什么会这样
PHP-FPM 子进程独立占用物理内存,超出限制会触发 Swap 交换或被系统 OOM Killer 强制终止。
每个 PHP-FPM 工作进程在处理请求时都会加载 PHP 解释器、扩展及业务代码到内存中,这部分内存是独占的。pm.max_children 限制了同时存在的最大子进程数。如果该值乘以单进程内存超过物理内存上限,操作系统会启用 Swap 导致性能急剧下降,或直接触发 OOM Killer 杀死进程,表现为网站 502 错误或服务重启。
分步处理
按照以下步骤计算并配置 pm.max_children 值:
- 确认系统总内存:执行
free -m,记录 total 列的数值(单位 MB)。 - 预留其他服务内存:减去 MySQL、Nginx、操作系统预留内存。保守建议预留总内存的 30% 给非 PHP 服务,若无数据库同机部署可预留 10%。
- 获取单进程内存:执行“命令速用版”中的 ps 命令,获取平均 RSS 值(单位 KB),转换为 MB(除以 1024)。
- 计算最大子进程数:公式为
(可用内存 MB * 1024) / 单进程内存 KB。结果向下取整。 - 修改配置文件:编辑 php-fpm 池配置文件(通常为
/etc/php-fpm.d/www.conf或/etc/php/版本/fpm/pool.d/www.conf),找到pm.max_children填入计算值。 - 重载服务:执行
systemctl reload php-fpm使配置生效。
怎么验证是否生效
配置完成后,通过以下指标确认设置是否合理:
- 进程数检查:执行
ps aux | grep php-fpm | wc -l,观察运行中的进程数是否接近设定值。 - 日志监控:检查 PHP-FPM 错误日志(通常位于
/var/log/php-fpm.log或/var/log/php-fpm/error.log),确认无“server reached max_children setting”警告。 - 系统日志:执行
dmesg | grep -i oom,确认无 OOM Killer 杀死 php-fpm 进程的记录。 - 业务表现:观察网站响应速度,若出现大量 502/504 错误且进程数未达上限,可能需调大该值;若系统频繁 Swap,需调小该值。
常见坑
- 动态模式干扰:若 pm 设置为 dynamic 或 ondemand,max_children 是上限而非固定值,需同时关注 pm.start_servers 和 pm.max_spawn_rate。
- 内存峰值误判:使用 ps 命令看到的是瞬时内存,业务高峰期单进程内存可能上涨 20%-50%,计算时建议预留缓冲空间。
- 同机数据库:若 MySQL 与 PHP-FPM 同机,MySQL 缓冲池(innodb_buffer_pool_size)会占用大量内存,必须先从总内存中扣除。
- Swap 误导:开启 Swap 虽能防止 OOM 崩溃,但频繁 Swap 会导致请求响应时间从毫秒级降至秒级,不应作为长期解决方案。
常见问题
pm 设置为 dynamic 时 max_children 还重要吗?
重要,max_children 在 dynamic 模式下代表进程数上限,超过该值新请求会排队或报错。
设置后出现 OOM 杀进程怎么办?
立即调小 pm.max_children 数值,或增加服务器物理内存,检查是否有内存泄漏的 PHP 脚本。
如何监控 PHP-FPM 内存趋势?
建议使用 Prometheus 配合 php-fpm exporter,或通过 Zabbix 脚本定期采集进程内存数据。