用户登录

技术文章

外部链接将在新标签页打开。

Nginx 与 PHP-FPM 调优实战:从瓶颈判定到超时顺序的可验证做法

一句话回答

调优应作为受控实验:先用 $upstream_response_time、FPM slowlog 与 listen queue 判定瓶颈在 Nginx、FPM 还是下游,再单变量改配置并回归验证。pm.max_children 由可用内存除以实测单进程 RSS 估算上限而非目标值;超时需让 PHP 层先于 FPM request_terminate_timeout 触发、Nginx fastcgi_read_timeout 兜底;连接复用要与进程数匹配。瓶颈在数据库时增加进程只会转移排队。

把 Nginx 与 PHP-FPM 调优当成“改几个参数”,通常得不到可复现的结果。更可靠的做法是把它当成一次受控实验:先固定观测口径,判断延迟发生在 Nginx、FPM 还是下游(数据库、外部接口);再单变量修改配置,用同一份负载做回归验证。本文只讲这个流程与几处参数耦合关系,不提供性能数字——本次没有可核验的测试记录,具体默认值和版本行为请对照所用版本的官方文档核实。

先确认瓶颈在哪一层#

  • Nginx 侧:$request_time 与 $upstream_response_time 差距大,说明时间耗在 Nginx 自身或客户端链路;两者接近,说明时间耗在 FPM。
  • FPM 侧:开启 slowlog 与 request_slowlog_timeout,可定位请求卡在哪个函数;pm.status_path 可观察 listen queue 长度与 active/idle 进程分布。
  • 系统侧:CPU、内存、磁盘 IO 与上下文切换,用来判断是否已到资源上限。

listen queue 持续非零,是进程不足的直接证据;但 CPU 已饱和时继续加进程,只会加重切换开销。这一步决定后面该调进程数,还是回头改代码和 SQL。

进程数:算上限,而不是抄数字#

pm.max_children 是硬上限,超过后请求进入 listen queue 排队。可用内存除以单个 worker 的常驻内存(RSS)可估算理论上限,但这不是目标值:还要给系统进程和其他服务留余量,且单进程 RSS 必须在真实负载下测量,空载值明显偏低。

pm = static 适合内存固定、流量平稳的场景,省去调度开销;pm = dynamic 适合波动较大的场景,但要同时设置 pm.max_requests,避免第三方扩展的内存泄漏持续累积。进程回收本身有成本,周期不宜设得过短。

超时是有先后顺序的#

超时配置最常见的问题是各层顺序颠倒,导致错误出现在不期望的层。一种通行约定是:让 PHP 层(max_execution_time)先于 FPM 的 request_terminate_timeout 触发,再由 Nginx 的 fastcgi_read_timeout 兜底。这样超时能以应用可捕获的形式暴露;若 FPM 先杀 worker,客户端往往只看到一个 504,排查线索更少。

另外要区分两个语义不同的参数:fastcgi_connect_timeout 是连上 FPM 的等待时间,fastcgi_read_timeout 是等待响应的时间,后者通常应显著大于前者。

连接复用要与进程数匹配#

# 未执行示例,仅示意参数关系,取值需按实际环境核实
upstream php_fpm {
    server 127.0.0.1:9000;
    keepalive 16;
}
server {
    location ~ \.php$ {
        fastcgi_pass php_fpm;
        fastcgi_keep_conn on;   # 需与 upstream 中的 keepalive 配合
        include fastcgi_params;
    }
}

连接复用减少每次请求的建连开销,但如果上游连接池超出 worker 的承受能力,收益会被排队抵消。上述指令的版本要求与 FPM 侧的配合方式待核实。

变更纪律#

  1. 一次只改一个参数,改动前后对比同一指标。
  2. 用固定脚本或压测工具复现同一负载,不要拿线上流量做对照。
  3. 改完先用 php-fpm -t 做语法检查,再做重载;保留可回滚的旧配置。

如果瓶颈在慢查询或外部接口,调 FPM 进程数只是把排队从 FPM 转移到数据库——这是这类调优里最常见的误判。

作者:沐小悠

本文约 4 分钟读完 · 关键词:Nginx 调优、PHP-FPM、pm.max_children、fastcgi_read_timeout、listen queue

更多文章 GET 示例接口

「」标签下的其他文章