Nginx 与 PHP-FPM 调优:没有实测数据时,先建立可复核的观测口径
本次没有可引用的官方文档正文与测试记录,因此文章不给推荐参数值,只给一份可执行的核实顺序:先用 Nginx 的上游耗时与 PHP-FPM 进程池指标区分瓶颈在应用侧还是接入侧,再决定是否调整进程数、超时与缓冲;所有具体取值都标注为需要按本机内存与并发实测确定。
这篇不提供“调优参数模板”。本次没有可引用的官方文档正文,也没有可核验的压测记录,因此任何“把某参数调到某值”的说法都属于猜测。可以给出的是一个约束明确的核实顺序:先分清慢在哪一侧,再判断该动进程池、超时还是缓冲,最后用同一套指标复核改动。下文出现的配置片段均为未执行示例,数值一律留空。
第一步:用两个时间字段拆分“慢”#
Nginx 的日志变量里,$request_time 表示请求在 Nginx 内的整体处理耗时,$upstream_response_time 表示 Nginx 等待上游(这里是 PHP-FPM)响应的时间。两者并列记录,才能回答“是 PHP 处理慢,还是接入侧慢”。变量的精确定义与可用版本,请以你所用 Nginx 版本的官方文档为准(本次未核验)。
# 未执行示例:放入 http 块,并在 server/location 中通过 access_log 引用
log_format timed '$remote_addr "$request" $status '
'rt=$request_time urt=$upstream_response_time';
如果 rt 远大于 urt,说明时间花在 Nginx 收发与客户端连接上,此时优先怀疑客户端慢、响应体积大或缓冲策略,而不是加 PHP 进程。如果两者接近且都偏高,才把注意力转向 PHP-FPM。
第二步:看进程池是否饱和#
PHP-FPM 的进程池有三个互相牵制的量:进程数上限(pm.max_children)、每个进程处理多少请求后重启(pm.max_requests)、以及监听队列长度。核心约束是内存:单进程常驻内存 × 进程数 必须给系统和其他服务留出余量。单进程内存占用需要在你自己的环境观察,本文不给数值。
; 未执行示例,数值必须按实测确定
pm = dynamic
pm.max_children = YOUR_VALUE
pm.max_requests = YOUR_VALUE
判断是否饱和,看的是“请求到达时有没有空闲 worker”,而不是 CPU 曲线。若队列持续堆积,加进程数只是把排队位置从 PHP-FPM 挪到内存压力上,需要先确认下游(数据库、外部接口)不是瓶颈。
第三步:衔接处的四个观察点#
- 传输方式:
fastcgi_pass指向 Unix socket 还是 TCP 地址,影响连接开销与隔离方式,取舍要按部署拓扑定。 - 读超时:
fastcgi_read_timeout过短会把慢请求变成 504,过长会让 worker 被长期占用。 - 缓冲:响应缓冲相关指令决定大响应体是否在 Nginx 内存/临时文件中转。
- OPcache:属于 PHP 侧,重载与失效策略会影响每次请求的编译开销。
一份最小观测清单#
| 指标 | 从哪里取 | 回答什么问题 | 本次取值 |
|---|---|---|---|
| request_time / upstream_response_time | Nginx access log | 慢在接入侧还是上游 | 待核实 |
| 活跃进程数与队列长度 | PHP-FPM 状态页 | 进程池是否饱和 | 待核实 |
| 单进程常驻内存 | 系统进程视图 | 进程数上限的可行区间 | 待核实 |
| 慢请求样本 | PHP-FPM 慢日志 | 慢在哪个脚本/调用 | 待核实 |
常见误判#
- 把
request_time直接当 PHP 耗时,于是误加进程数。 - 只调大
pm.max_children,不核对内存余量。 - 用平均值判断,掩盖了尾部长请求;应看分位与慢日志样本。
- 在生产直接改配置验证。建议先在可回滚的环境里改一项、记一组指标。
边界说明:本文未运行任何压测,不给吞吐、延迟或并发数字;不同 PHP 与 Nginx 版本的指令行为可能不同,落地前请对照各自官方文档逐项确认。