Nginx 与 PHP-FPM 调优先算什么:进程池容量、超时链与变更顺序
Nginx 与 PHP-FPM 调优不应从改参数开始,而应先分层定位瓶颈,再把进程池容量换算成内存与后端并发预算,并按“内层先失败”的原则对齐超时链;具体数值必须在目标环境压测后确定,本文不含实测结论。
先给结论:在没有可比基线的前提下,Nginx 与 PHP-FPM 的“调优”不该从改参数开始,而应先做三件事:确认瓶颈在哪一层、把进程池容量换算成内存与后端并发预算、把 Nginx 到 FPM 的超时链按“内层先失败”对齐。本文不含实测数字:本次没有可用的正文素材,也没有可核验的压测记录,因此所有取值都要在你的目标环境测量后确定,参数名与默认值请以所部署版本的官方文档为准(待核实)。文中配置片段均为未执行的占位示例。
一、先判断瓶颈在哪一层,而不是先加进程#
一个 PHP 请求在链路上至少经过四段:Nginx 连接与 worker、FastCGI 排队与 PHP-FPM 进程池、PHP 脚本执行、脚本调用的外部依赖(数据库、缓存、HTTP 接口)。四段的失败表现不同,混在一起看会得出错误结论。
- 连接层:Nginx 侧连接被拒或排队,通常指向 worker 连接上限、文件描述符或内核队列。
- 进程池层:请求在 FPM 侧等待空闲进程,表现为延迟上升但 CPU 不高。
- 执行层:脚本本身的 CPU 或内存消耗,可用慢日志定位。
- 依赖层:脚本在等外部响应,FPM 进程被长时间占住。
判断依据应来自你自建的观测:Nginx 的访问与错误日志、PHP-FPM 进程池状态页、慢请求日志、以及系统层的 CPU、内存、负载与连接数。状态页需在进程池配置中显式开启,具体指令与字段随版本而异,请按版本文档核对,不要照抄他人配置。
二、把进程池参数换算成资源等式#
PHP-FPM 的 pm.max_children 决定同一时刻最多有多少个子进程处理请求,它同时受两侧约束:
- 内存侧:
max_children × 单进程常驻内存 ≤ 为 PHP 预留的内存预算。单进程内存必须实测,不同框架差异很大,本文不给数字。 - 下游侧:
max_children × 每请求持久的数据库连接数 ≤ 数据库可承受的连接上限。进程池放宽后,压力会原样传导到下游。
因此“加大 max_children 后变快”往往只是把排队从 FPM 挪到了数据库。如果慢的根因是 SQL 或外部接口,扩大进程池只会放大并发、加重下游负担。pm.max_requests 用于周期性回收进程,取值需要权衡内存泄漏与重启开销;pm 采用 static、dynamic 还是 ondemand,取决于流量是否平稳,这一点同样需要你的流量曲线来定。
; 占位示例,未在目标环境执行;数值须由压测与内存测量确定
pm = dynamic
pm.max_children = YOUR_MAX_CHILDREN
pm.start_servers = YOUR_START_SERVERS
pm.max_requests = YOUR_MAX_REQUESTS
request_terminate_timeout = YOUR_SECONDS
三、超时链要对齐,让内层先失败#
很多“调优后更慢”的案例,本质是超时配置互相矛盾:Nginx 先超时返回 504,但 PHP 进程仍在校验、写入或重试,资源被继续占住。原则是外层超时不短于内层,并让失败点能留下可归因的日志。
| 层 | 常见参数(按版本文档核对) | 期望的失败顺序 |
|---|---|---|
| PHP 脚本 | max_execution_time、set_time_limit | 最先触发 |
| FPM 进程池 | request_terminate_timeout | 脚本超时后的后备措施 |
| Nginx → FPM | fastcgi_connect_timeout、fastcgi_read_timeout | 最后触发,返回 504 并写错误日志 |
| 上游客户端 | 业务侧 HTTP 客户端超时 | 与最内层保持一致 |
注意 max_execution_time 在不同 SAPI 下的计时范围并不相同,具体行为请查对应版本文档(待核实)。若脚本里有阻塞式外部调用,脚本超时可能无法及时中断,此时应以客户端超时为主。
# 占位示例,未执行
location ~ \.php$ {
fastcgi_connect_timeout YOUR_CONNECT_SECONDS;
fastcgi_read_timeout YOUR_READ_SECONDS;
fastcgi_pass unix:/YOUR_SOCKET_PATH;
}
四、变更顺序与实验边界#
- 记录基线:固定负载模型(并发数、请求分布、数据规模),记录 p95 延迟、错误率与各层资源占用。
- 一次只改一个维度:容量、超时、还是应用层缓存,分开改才可归因。
- 同负载复测并保存原始数据;没有复测的“感觉变快了”不算结论。
- 准备回滚:配置变更前留存原文件,并明确回滚判据(例如错误率超过基线多少即回退)。
篇幅与素材所限,本文只能提供方法与边界,不能提供“某参数设为某值”的通用答案。若你需要一组可直接上线的数值,需要先补齐两样东西:目标环境的单进程内存测量值,以及下游系统的连接上限。