用户登录

技术文章

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

Nginx 与 PHP-FPM 性能调优:先测出瓶颈,再动参数

一句话回答

调优顺序应是先定位瓶颈在连接、Nginx、PHP-FPM 还是应用下游,再改参数。PHP-FPM 的容量上限由可用内存与单 worker 常驻内存共同决定,必须在目标机器上测量而非套用他人数值;Nginx 侧要关注到上游的连接复用与 FastCGI 缓冲落盘;Nginx、FPM 与应用的超时链必须逐层对齐,上游超时应短于调用方。本文不含实测数据与通用推荐值。

Nginx 与 PHP-FPM 的性能调优,第一步不是改配置,而是把“慢在哪一层”测出来。本文没有可核验的版本信息与基准数据,因此不给通用推荐数值,只给核对顺序、参数含义和容易互相冲突的边界。本文未运行任何配置或压测,出现的配置片段均为未执行的占位示例。

先把瓶颈定位到具体一层#

一个动态请求至少经过四段:客户端到 Nginx 的连接、Nginx 自身、Nginx 到 PHP-FPM 的 FastCGI 调用、PHP 应用及其下游(数据库、缓存、外部接口)。如果应用里一次查询就占了几百毫秒,调大进程数只会让更多请求同时排队。

  • Nginx 访问日志中的 $request_time 与 $upstream_response_time:两者差距明显,说明时间耗在 PHP 侧或下游;两者接近,则更可能在 Nginx 之前的连接环节或 Nginx 自身。
  • PHP-FPM 状态页(由 pm.status_path 暴露):重点看 listen queue、active/idle 进程数与 max children reached。队列长期大于 0 是容量不足的直接信号,达到上限的次数增长说明进程池被打满。
  • 系统层:CPU、内存、swap、磁盘 IO。一旦出现 swap,增加进程通常会让情况更糟。

这三类观测要在同一时间窗对照。没有变更前后的同口径数据,任何“变快了”都无法归因。

PHP-FPM:进程模型决定容量上限#

pm 有三种模式,取舍在于内存与延迟:

  • static:进程数固定,启动即分配,延迟稳定、内存占用固定,适合负载平稳且内存可预估的场景。
  • dynamic:按 pm.start_servers、pm.min_spare_servers、pm.max_spare_servers 增减,空闲时省内存,突发时有扩容延迟。
  • ondemand:空闲进程退出,最省内存,但冷启动会增加延迟,适合低频站点。

pm.max_children 是硬上限,粗算方式是“可分配给 PHP 的内存 ÷ 单个 worker 常驻内存”。这两个量都必须在目标机器上测:常驻内存随框架、扩展、opcache 配置变化很大,用别人的数字代替会直接算错。上限超过机器实际能承受的范围,结果是 swap 和整体变慢。

另外两个常被忽略:pm.max_requests 会在达到请求数后回收 worker,可缓解长期运行的内存增长,但设得过小会带来持续的重建开销;request_terminate_timeout 是 FPM 侧的兜底,必须与 Nginx 的读取超时和应用执行时间上限一起看。

Nginx:连接、缓冲与到上游的复用#

  • worker_processes 与 worker_connections 共同决定并发连接容量;后者包含到上游的连接,不只是客户端连接。
  • 到上游的 keepalive:在 upstream 块配置连接池,并让发往上游的请求携带 keep-alive 语义,可减少反复建连。池大小与空闲超时需按 QPS 和上游处理时间估算,并非越大越好。
  • FastCGI 缓冲(fastcgi_buffers、fastcgi_buffer_size、fastcgi_busy_buffers_size):响应头或响应体超出缓冲会落盘,落盘通常显著变慢。应对方式是按实际响应体积调整缓冲,而不是关掉缓冲。
  • 把静态资源交给 Nginx 直接返回,让 PHP-FPM 只处理必须由它处理的部分。

超时参数必须逐层对齐#

Nginx 的 fastcgi_connect_timeout、fastcgi_send_timeout、fastcgi_read_timeout,PHP 的 max_execution_time,FPM 的 request_terminate_timeout,以及应用调用下游客户端的超时,构成一条链。若 FPM 先终止进程而 Nginx 仍在等待,客户端看到的是网关错误而非明确原因;若 Nginx 先断开,PHP 可能继续跑完,白白占用容量。可操作的原则是:上游超时短于调用方超时,让错误在能记录、能处理的那一层暴露。

配置结构示意(未执行)#

# 仅示意参数位置,数值为占位,未在任何环境执行
upstream php_backend {
    server unix:/run/php-fpm.sock;
    keepalive 16;              # 占位值,按实测调整
}

location ~ \.php$ {
    fastcgi_pass php_backend;
    fastcgi_read_timeout 30s;  # 占位值,需短于调用方超时
    fastcgi_buffers 16 16k;    # 占位值,按响应体积调整
    include fastcgi_params;
}

常见误区#

  • 只调 pm.max_children 而不看内存与下游:进程变多,数据库连接数和锁竞争也会一起上升。
  • 把 opcache 当可选项:脚本重复编译的开销,通常比调整进程数更值得优先处理。其内存上限与可缓存文件数要按代码规模设置。
  • 把页面缓存、对象缓存、opcache 混在一起谈,改一层后看不出效果,也无法判断是哪一层起了作用。
  • 压测只打首页或单一接口,得到的容量不能直接外推到混合流量。

边界说明#

本文不含任何实测数字、版本差异或推荐配置。不同 Nginx 与 PHP 版本、不同扩展组合下,参数的默认值与语义可能变化,具体以所部署版本的官方文档为准(本次未核验)。若要在生产环境调整,建议一次只改一组参数,保留变更前后的同口径对照数据,并事先准备回滚方案。

作者:沐小悠

本文约 6 分钟读完 · 关键词:Nginx 调优、PHP-FPM 调优、pm.max_children、FastCGI 超时、fastcgi 缓冲

更多文章 GET 示例接口

「」标签下的其他文章