Nginx 与 PHP-FPM 调优:先定位排队层,再改参数
调优应从定位瓶颈层级开始:先用监控区分 Nginx 连接、PHP-FPM 进程池与应用内部等待,再调整 keepalive、缓冲、进程数与超时。参数值需由压测与回滚验证,不能照抄。
Nginx 与 PHP-FPM 的调优,不是寻找一组“最优参数”,而是先判断请求排在哪里:是 Nginx 的工作连接不够,是 PHP-FPM 进程池被打满,还是 PHP 应用在等数据库或外部接口。本文没有可用的正文材料,也不声称联网核验,因此只讨论通用的定位方法与参数之间的关系;具体版本的最优值需要结合监控和压测确定,待核实。
第一步:分清瓶颈在哪一层#
Nginx 是多进程事件驱动服务器,PHP-FPM 是 FastCGI 进程管理器。两者之间的队列通常比参数本身更值得先看。可以按下面的顺序排查:
- Nginx 层:活跃连接数、读写等待连接数、listen 队列是否溢出,以及 worker 进程的 CPU 占用。
- PHP-FPM 层:进程池的 active/idle 进程数、listen queue 长度、是否触发 max children reached,以及 slowlog 中记录的慢请求。
- 应用层:数据库慢查询、外部 HTTP 调用、锁等待。若应用本身在阻塞,增加 PHP-FPM 进程只会把更多请求堆在数据库前。
- 系统层:内存是否紧张、是否使用 swap、上下文切换和网络丢包情况。
一个实用原则是:先看“等待发生在哪里”,再看“资源是否耗尽”。CPU 不高但请求堆积,往往说明进程池不足或上游阻塞;CPU 打满而队列不长,则更可能是应用计算或 worker 配置问题。
Nginx 侧:连接复用与缓冲#
Nginx 到 PHP-FPM 的连接可以复用,减少反复建立连接的开销。对 FastCGI upstream,常见做法是在 upstream 块声明 keepalive,并在 location 中开启 fastcgi_keep_conn。这不是无条件收益:连接数、超时和 PHP-FPM 的接受能力要一起看。
反向代理场景还要关注缓冲。开启 proxy_buffering 可以让 Nginx 先接收上游响应,尽快释放上游连接;但大文件下载、流式响应或需要低延迟透传的接口,可能需要关闭缓冲或调整 buffer 大小,否则会写临时文件或增加内存压力。
超时参数不宜盲目调大。把 fastcgi_read_timeout 或 proxy_read_timeout 设得很长,故障时会让 worker 和 PHP-FPM 进程被占住更久,反而放大排队。
PHP-FPM 侧:进程模型与内存上限#
pm 的三种常见模式是 static、dynamic 和 ondemand。static 进程数固定,适合负载稳定、内存可预测的场景;dynamic 按空闲进程伸缩,适合波动负载;ondemand 在请求到来时才创建进程,省内存但可能增加冷启动延迟。选择依据是流量形态,不是“哪个更高级”。
pm.max_children 是硬上限。可以用下面的方法估算,但结果只是起点:
可用内存 × 安全系数 ÷ 单个 PHP-FPM 进程的常驻内存(RSS)≈ max_children 上限
单进程 RSS 需要在真实请求下观察,不能只看空进程。安全系数要留给系统缓存、Nginx 和其他服务。超过内存上限后,进程被换出或触发 OOM,延迟会比排队更糟。
pm.max_requests 可以让进程定期重启,缓解内存泄漏;代价是重启期间的处理开销。是否设置、设置多大,应结合内存曲线和请求特征判断。request_terminate_timeout 与 slowlog 则用于兜底:前者终止失控请求,后者帮助定位慢在哪里。
示意配置与验证方式#
下面的片段仅用于说明参数位置,未在特定 Nginx/PHP-FPM 版本上执行,数值为演示值,需按环境替换并验证。
upstream php_backend {
server unix:/run/php-fpm/app.sock;
keepalive 32;
}
server {
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass php_backend;
fastcgi_keep_conn on;
fastcgi_read_timeout 30s; # 演示值
}
}
[app]
pm = dynamic
pm.max_children = 50 ; 演示值,需按内存与压测确定
pm.start_servers = 10
pm.min_spare_servers = 10
pm.max_spare_servers = 20
pm.max_requests = 500
request_terminate_timeout = 30s
slowlog = /var/log/php-fpm/slow.log
request_slowlog_timeout = 2s
验证时一次只改一组参数,记录改动前后的吞吐、p95/p99 延迟、错误率和内存曲线,并保留回滚配置。压测工具和测试环境需与生产接近;没有可核验的测试记录,就不应把“调完之后变快了”当作结论。
边界条件#
参数调优不能替代慢查询优化和外部依赖治理。若 slowlog 显示时间花在数据库或 HTTP 调用上,优先修应用链路;若 PHP-FPM 的 listen queue 持续增长,先确认是进程不足还是下游阻塞,再决定是否增加 max_children。至于具体版本支持的指令语义和默认值,应以所用版本的官方文档为准;本次材料未提供,待核实。