用户登录

技术文章

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

Nginx 与 PHP-FPM 调优:先定位瓶颈,再决定改哪个参数

一句话回答

Nginx 与 PHP-FPM 调优应先区分连接层、转发层、执行层与超时层的信号,再改参数。pm.max_children 受内存约束,FPM 终止超时与 Nginx 读取超时不应倒挂,listen.backlog 需与内核 somaxconn 匹配。每次只改一个参数,并用状态页、慢日志、stub_status 与 socket 队列留下对照。具体取值依赖版本、硬件与流量模型,本文未联网核验,也未执行压测。

核心要点 TL;DR
  • 先看 FPM 活跃进程是否已达上限,再判断是容量问题还是脚本执行慢。
  • Nginx 的 fastcgi_read_timeout 若短于 FPM 的 request_terminate_tim
  • 一次只改一个参数,并用同一压测条件保留改动前后的对照记录。

Nginx 与 PHP-FPM 调优最常见的失败不是某个参数写错,而是顺序错了:还不知道请求卡在哪一层,就先改 pm.max_children 或 worker_connections,改完既说不清是否变快,也无法复现。可行的顺序是先把「排队、执行、超时、内存」四类信号分开观测,再决定动哪个参数。本文讨论判定顺序与参数间的耦合关系;具体取值取决于 PHP/Nginx 版本、内存与流量模型,属于待核实项。本文未联网核验,也未执行任何压测或配置加载。

一、先确认请求卡在哪一层#

一次 PHP 请求经过三段:Nginx 接收连接、转发给 FPM、FPM 子进程执行脚本。三段各有自己的排队点。

  • Nginx 连接层:worker_connections 用尽时,新连接被拒绝或排队,Nginx 错误日志会出现连接相关报错。
  • 转发层:FPM 进程池无空闲 worker 时,请求进入 listen 队列等待;队列满后连接被拒。
  • 执行层:脚本本身慢,如数据库查询、外部 API、磁盘 IO。此时 worker 被占住,会反过来加剧转发层的排队。

判据是:如果请求变慢的同时 FPM 活跃进程数已顶到 pm.max_children,问题在池容量或执行时长;如果活跃进程数远低于上限而响应仍慢,应先查脚本与下游依赖,而不是加进程。

二、参数之间是耦合的#

  • pm.max_children 受内存约束。子进程数乘以单进程常驻内存后,要为系统和 Nginx 留出余量;容器环境还要对齐内存限制,否则 OOM killer 会替你“调参”。
  • request_terminate_timeout(FPM 终止脚本)与 fastcgi_read_timeout(Nginx 等待上游)不应倒挂。若 Nginx 先超时,客户端拿到 504,而 FPM 里的脚本仍在跑,形成看不见的负载。
  • listen.backlog 要与内核 somaxconn 匹配,单方面调大没有意义。
  • Nginx 到上游的 keepalive 与 FPM 的 pm.max_requests 都会改变连接复用行为,两者同时改会掩盖各自的贡献。

三、一个可改的配置骨架(未执行示例)#

下面是结构示意,值全部为占位符,未在任何环境加载执行。

; php-fpm pool 片段(占位,未执行)
pm = dynamic
pm.max_children = ${MAX_CHILDREN}
pm.start_servers = ${START}
pm.min_spare_servers = ${MIN_SPARE}
pm.max_spare_servers = ${MAX_SPARE}
pm.max_requests = ${MAX_REQUESTS}
request_terminate_timeout = ${FPM_TIMEOUT}
request_slowlog_timeout = ${SLOW_TIMEOUT}
slowlog = /var/log/php-fpm/slow.log
pm.status_path = /fpm-status
# nginx 片段(占位,未执行)
location ~ \.php$ {
    fastcgi_pass unix:/run/php-fpm.sock;   # 或 127.0.0.1:9000
    fastcgi_read_timeout ${NGINX_TIMEOUT}; # 建议大于 FPM_TIMEOUT
    include fastcgi_params;
}

四、一次只改一个,并留下对照#

可用的观测点包括:FPM 状态页(活跃/空闲进程、队列长度)、FPM 慢日志、Nginx stub_status,以及 ss -lnt 观察 listen socket 的 Send-Q 是否持续堆积。每次只改一个参数,在同一压测条件下记录改动前后的指标;没有对照的“感觉快了”不构成结论。

五、边界条件#

  • 不要照抄他人的 pm.max_children:它取决于单进程内存与业务执行时长。
  • static 减少进程管理开销但常驻内存高;ondemand 省内存但冷启动有延迟,适合低频池。
  • 不同大版本的默认值与可用指令存在差异,具体语义以所装版本的官方文档为准;本文未核验版本细节。

一句话收束:调优的产出不是一组“最佳参数”,而是一条能重放的判定路径——先知道瓶颈在哪,再让参数去匹配它。

常见问题(FAQ)

pm.max_children 应该设多大?
它由单进程常驻内存与可用内存共同决定,还要给系统和 Nginx 留余量;容器中需对齐内存限制。具体数值必须基于本机单进程内存测量,本文不提供通用数字。
出现 504 时该先查 PHP 还是 Nginx?
先比对两侧超时配置:若 Nginx 等待时间短于 FPM 终止脚本的时间,504 可能只是 Nginx 先放弃,脚本仍在执行。再结合 FPM 慢日志判断脚本本身是否慢。

作者:沐小悠

本文约 5 分钟读完 · 关键词:Nginx 调优、PHP-FPM、pm.max_children、fastcgi_read_timeout、FPM 状态页

更多文章 GET 示例接口

「」标签下的其他文章