PHP-FPM 高峰排队时怎样估算进程上限
PHP-FPM 高峰排队时,pm.max_children 不应该按“CPU 核数乘一个倍数”直接拍板。它首先是并发请求上限,同时还会把每个 PHP 子进程占用的内存、数据库连接和 CPU 争用放大。比较稳妥的做法是:先算出内存上限,再分别算 CPU 和下游连接上限,最后取最小值,再用 status 页面观察排队是否真的改善。
官方地址:https://www.php.net/manual/en/install.fpm.configuration.php
下面的数字只是演示估算方法,不是某台机器的实测结果。本文把 FPM 子进程和下游服务当作需要保护的资产,把“请求堆在 listen queue 中”当作风险信号,重点回答一个问题:怎样设一个能解释、能回滚的起始值。
先确定真正要保护的资源
一次 PHP 请求可能同时占用一个 FPM 子进程、一段进程 RSS、一个或多个数据库连接,以及 CPU 时间。把 pm.max_children 继续调大,只会让更多请求进入执行态;如果数据库已经没有连接槽位,或者每个子进程都很重,队列可能从 FPM 移到别处,整机还会开始交换。
因此先列出四个约束:
- 内存:留出操作系统、Web 服务器、数据库客户端、缓存和安全余量后的内存,才能给 FPM。
- CPU:高并发不是免费吞吐。CPU 密集型脚本的并发上限通常比 I/O 等待型脚本更低。
- 下游连接:如果每个请求都要占用数据库连接,FPM 子进程数不能超过连接池和数据库剩余预算。
- 排队可接受度:短时队列可以被吸收,持续增长的队列说明当前容量或请求耗时有问题。
用进程内存估算第一道上限
先观察业务高峰时 FPM 子进程的高位 RSS,而不是拿一个刚启动进程的空闲内存做除数。可以用一段时间内的高位样本,或者从进程监控中取接近 P95 的值。估算式如下:
# 可分配给 FPM 的预算 = 总内存 - 系统与旁路服务 - 安全余量
# 内存上限取整,避免把小数进程当成可用容量
内存上限 = floor(FPM 可用内存 / 单个子进程高位 RSS)
例如,一台 8 GB 主机预留 2 GB 给系统和其他服务,再留 512 MB 安全余量,得到约 5.5 GB 的 FPM 预算。如果单个子进程的高位 RSS 约为 180 MB:
# 这里的数字是示例,实际应替换成业务高峰采样值
FPM 预算 = 8192 - 2048 - 512 = 5632 MB
内存上限 = floor(5632 / 180) = 31
这个 31 不是最终配置,而是第一道不能轻易越过的边界。若 PHP 扩展、图片处理或某类慢请求会使 RSS 明显变大,应使用更高位的样本;否则平时看起来正常,高峰时仍可能被内存反噬。

把 CPU 与下游连接数加入取小值
内存上限只说明“装得下多少个进程”,还不说明这些进程能否有效工作。可以为 CPU 和连接数各算一个保守的候选值:
# CPU 候选值只用于建立起始边界,不替代压测
CPU 上限 = floor(可分配 CPU 容量 / 单请求平均 CPU 消耗)
# 连接预算要扣除管理后台、定时任务和其他 PHP 池占用
连接上限 = floor(数据库可用连接数 / 每个请求平均连接占用)
# 最终起始值取所有资源上限中的最小值
pm.max_children = min(内存上限, CPU 上限, 连接上限)
继续用示例说明:如果 CPU 预算折算后得到 24,数据库还允许这个池使用 20 个并发连接,那么最终起始值应是 min(31, 24, 20) = 20,而不是把 31 直接写进配置。若应用使用连接池或一个请求会串行访问多个下游,连接预算要按实际占用方式修正。
这个取最小值的动作是保护边界:它牺牲一部分理论并发,换来更可控的内存和下游压力。对 CPU 等待少、外部 I/O 多的应用,CPU 上限可能不是最小值;对复杂报表和图片处理请求,CPU 或内存往往会先成为瓶颈。
用 FPM 状态页观察排队风险
PHP-FPM 的 status 页面可以告诉你当前池里有多少活跃、空闲进程,以及请求是否在等待空闲进程。PHP 官方文档列出的关键字段包括 listen queue、max listen queue、active processes、idle processes、max active processes 和 max children reached。
建议把这些字段放在同一个时间窗口看:
listen queue持续大于 0,说明当前有请求等空闲进程;如果同时 active 接近上限,FPM 并发边界很可能偏小。max children reached表示历史上曾经碰到过进程上限,但它本身不能证明调大后一定更快。- active 很低但请求仍慢,优先查数据库、外部 HTTP、锁或单请求耗时,不要只增加进程数。
- idle 长期很多而 queue 没有积压,说明当前值可能偏宽松,但仍要结合内存余量再缩小。
; 只给内部监控路径使用,避免把资源信息暴露给公网
pm.status_path = /fpm-status
; dynamic 让空闲进程和启动进程数在边界内变化
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 8
status 页面应该限制为内网或已知监控来源。它会暴露池的资源信息,不能把它当成普通业务页面公开。

用受控配置和回滚方式落地
把估算值落地时,先保存原配置,再一次只改一个变量。建议顺序是:记录当前 pm.max_children 和资源曲线;设置一个略低于多项上限最小值的起始值;在同一高峰窗口观察 queue、RSS、CPU、数据库连接和错误率;确认没有持续积压后,再小步增加。
# 先备份当前池配置,保留可回滚副本
cp /etc/php/8.3/fpm/pool.d/www.conf /etc/php/8.3/fpm/pool.d/www.conf.bak
# 修改后先检查配置语法,再平滑重载;路径按发行版实际位置调整
php-fpm8.3 -t
sudo systemctl reload php8.3-fpm
如果内存开始逼近预算、数据库连接经常打满,或者 queue 下降但整体延迟反而上升,应先把值调小;如果 queue 只在瞬时尖峰出现、资源仍有充足余量,再考虑小步上调。回滚时恢复备份并重新检查配置,不要连续多次改动让比较失去基线。
结论:把进程上限当成资源预算
一个可解释的起始公式是:
# 先分别计算每个资源的安全上限,再取最小值
pm.max_children = min(
floor(FPM 可用内存 / 子进程高位 RSS),
CPU 并发预算,
数据库连接预算
)
然后用 status 页判断“排队是否还在、进程是否触顶、下游是否被压满”。pm.max_children 的目标不是让 FPM 永远没有队列,而是在可接受延迟内消化流量,同时把内存、CPU 和下游连接留在可恢复范围。
常见追问
只看服务器内存够不够吗?
不够。内存只能给出一个硬边界,CPU 和数据库连接可能更早成为瓶颈;最终仍应取多个候选上限中的最小值。
看到 max children reached 就应该调大吗?
不应该直接调大。先看 queue 是否持续、active 是否饱和,以及 RSS、CPU 和下游连接是否还有余量。
动态模式下 pm.max_children 还重要吗?
重要。动态模式只是让进程数在其他参数配合下变化,pm.max_children 仍是最大子进程数。
PHP OPcache 文件更新后未生效怎么办
- 上一篇
- PHP OPcache 文件更新后未生效怎么办
- 下一篇
- PHP 怎样统一记录 Error 和 Exception
-
- 文章 · php教程 | 1小时前 | pdo · php教程 · 异常排查 · php pdo 参数类型 预处理语句 bindParam bindValue PDOStatement PDO::PARAM_INT
- PHP PDO 怎样避免 bindValue 类型导致查询异常
- 381浏览 收藏
-
- 文章 · php教程 | 1小时前 | php教程 · php locale 金额格式化 ICU NumberFormatter formatCurrency ISO 4217
- PHP 金额格式化时怎样指定货币和区域
- 216浏览 收藏
-
- 文章 · php教程 | 1小时前 |
- PHP 怎样统一记录 Error 和 Exception
- 321浏览 收藏
-
- 文章 · php教程 | 1小时前 |
- PHP OPcache 文件更新后未生效怎么办
- 368浏览 收藏
-
- 文章 · php教程 | 1小时前 |
- PHP stream_select 怎样处理超时返回零
- 485浏览 收藏
-
- 文章 · php教程 | 1小时前 | 内存管理 · php教程 · php php://temp php://memory 临时流 内存阈值
- PHP php://temp 怎样控制内存与临时文件切换
- 310浏览 收藏
-
- 文章 · php教程 | 1小时前 |
- PHP SensitiveParameter 怎样避免参数进入堆栈输出
- 170浏览 收藏
-
- 文章 · php教程 | 1小时前 |
- PHP readonly class 怎样限制属性修改
- 395浏览 收藏
-
- 文章 · php教程 | 1小时前 | PHP · 字符串处理 · 输入校验 · php 字符串判断 PHP 8 str_contains
- PHP str_contains 处理空字符串时怎样理解结果
- 423浏览 收藏
-
- 文章 · php教程 | 1小时前 |
- PHP array_is_list 怎样判断连续整数键数组
- 361浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 510次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 570次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 595次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 547次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 364次使用
-
- Go sql.DB.SetConnMaxIdleTime 怎么淘汰长期空闲连接
- 2026-10-07 271浏览
-
- Go goroutine 泄漏怎么排查:pprof 快照、止血和回退手册
- 2026-07-09 374浏览
-
- sql.DB 为什么不是一条连接,池参数应按什么容量估算
- 2026-10-08 243浏览
-
- 使用 docker 搭建 web 服务环境
- 2023-01-24 483浏览
-
- 详解Mac OS用brew安装LAMP开发环境的步骤
- 2023-01-20 470浏览
