PHP-FPM 进程模型
PHP-FPM 用多个 worker 进程处理请求,常用三种 pm 模式:dynamic(固定范围内的动态进程)、ondemand(按需启动)、static(固定数量)。每个 worker 都是独立进程,会各自加载 PHP 与扩展,单进程内存通常在 20-60MB 之间,站点插件越多、扩展越重,占用越高。总内存 ≈ worker 数量 × 单进程内存,这就是调参的核心公式。
max_children 怎么算
先测单个 worker 的实际内存(可通过 ps 或 /proc 汇总估算),再用「服务器可用内存 ÷ 单进程内存」得到上限,并留出 30% 余量给 MySQL、系统缓存与其他服务。2GB 内存的小机器,如果 MySQL 与站点同机,max_children 往往只能设到 10-20;盲目调到 50 会直接触发 OOM。有 MySQL 独立部署或对象缓存占内存较大时,要相应调低。
OOM Killer 现象
当系统内存耗尽,内核的 OOM Killer 会挑内存占用高的进程直接杀掉,现象通常是 PHP-FPM 或 MySQL 突然消失、站点 502/504。确认方式:dmesg | grep -i “killed process” 或 journalctl -k 查看内核日志,若出现 Killed process 即可确认。此时不能只重启服务了事,必须找到内存持续增长的来源。
排查内存持续增长
- 观察 PHP-FPM 总内存随时间的变化曲线
- 若单个 worker 内存持续上涨,多半是某个扩展或代码存在泄漏
- 开启 slowlog 查看执行超过阈值的脚本,定位异常长任务
- 检查是否有大数组处理、一次性读入大文件、无 unset 的循环
- 确认 OPcache 已启用,避免重复编译 PHP 代码
参数建议
- 内存紧张:pm=ondemand,减少空闲时的常驻进程
- 流量稳定:pm=dynamic,设置合理 min/max/start 子进程数
- 高内存机器:pm=static + 足够 max_children,省去动态调度开销
- request_terminate_timeout 设置上限,杀掉卡死请求
- slowlog 必开,否则长脚本无从发现
别忽略的两点
一是 OPcache 与 APCu 能显著降低重复开销,确认 phpinfo 中已启用且内存配额合理。二是避免把 MySQL 的 buffer pool 设得过大与 PHP 抢内存,同机部署时两者要一起规划。改动参数后用压测验证:既要看响应时间,也要盯住内存曲线是否平稳——只有压测通过的配置才值得上线。