当前位置:首页 >  IDC >  服务器 >  正文

PHP-FPM 进程耗尽:pm.max_children 用内存反推怎么算

 2026-09-04 12:06  来源: 互联网   我来投稿 撤稿纠错

  一键部署OpenClaw

间歇性 502 里最磨人的一种:低峰期一切正常,流量一上来就崩,过会儿又自己好了。FPM 日志里翻出 server reached pm.max_children,基本就能确诊——子进程开到上限,新请求没进程可用了。

很多教程看到这条日志就让你把 pm.max_children 调大,却不说调到多少。拍脑袋填个 100,机器 2G 内存,结果就是 502 治好了,OOM 上门了——内存不够,内核开始杀进程,这次死的可能是 MySQL。

正确的算法是拿内存反推。先量出一个 PHP 子进程实际占多少内存,再用可用内存除一下。所谓可用内存,是总内存减去系统开销、减去 Nginx、减去 MySQL 这类常驻服务,剩下的才轮得到 PHP 分。

量单进程内存有个讲究:不能直接看 ps 里的 RSS 平均值,因为刚启动的进程占用小,跑过一轮请求的进程占用大。要取跑过请求之后的稳态值,取所有进程的 RSS 平均值作为参考,再留两成余量。

<pre style="background-color:#282c34;color:#abb2bf;padding:16px 20px;border-radius:8px;overflow-x:auto;font-family:Consolas,'Courier New',monospace;font-size:14px;line-height:1.7;margin:16px 0;-webkit-overflow-scrolling:touch;"><code style="font-family:inherit;"><span style="color:#7f848e;font-style:italic">#!/bin/bash</span>
<span style="color:#7f848e;font-style:italic"># pm.max_children 内存反推计算</span>
<span style="color:#7f848e;font-style:italic"># 用法:bash calc_max_children.sh</span>
<span style="color:#7f848e;font-style:italic"># 参数:可用给 PHP 的内存(MB),不填则按当前空闲内存的 60% 估算</span>

RESERVE_MB=${<span style="color:#d19a66">1</span>:-<span style="color:#d19a66">0</span>}

<span style="color:#7f848e;font-style:italic"># 1. 取所有 php-fpm 子进程的 RSS,算平均值(KB)</span>
AVG_KB=$(ps -o rss= -C php-fpm <span style="color:#d19a66">2</span>&gt;/dev/<span style="color:#56b6c2">null</span> | awk <span style="color:#98c379">&quot;{s+=\$1; n++} END {if(n&gt;0) printf \&quot;%d\&quot;, s/n; else print 0}&quot;</span>)

<span style="color:#c678dd">if</span> [ <span style="color:#98c379">&quot;<span style="color:#e06c75">$AVG_KB</span>&quot;</span> -eq <span style="color:#d19a66">0</span> ]; then
<span style="color:#c678dd">echo</span> <span style="color:#98c379">&quot;没取到 php-fpm 进程,确认服务是否运行&quot;</span>; <span style="color:#c678dd">exit</span> <span style="color:#d19a66">1</span>
fi

AVG_MB=$((AVG_KB / <span style="color:#d19a66">1024</span>))

<span style="color:#7f848e;font-style:italic"># 2. 可用内存:没指定就按空闲内存的 60% 算(保守留余量)</span>
<span style="color:#c678dd">if</span> [ <span style="color:#98c379">&quot;<span style="color:#e06c75">$RESERVE_MB</span>&quot;</span> -eq <span style="color:#d19a66">0</span> ]; then
FREE_MB=$(free -m | awk <span style="color:#98c379">&quot;/^Mem:/ {print \$7}&quot;</span>)
RESERVE_MB=$((FREE_MB * <span style="color:#d19a66">60</span> / <span style="color:#d19a66">100</span>))
fi

<span style="color:#7f848e;font-style:italic"># 3. 反推进程数,再打八折留安全边界</span>
RAW=$((RESERVE_MB / AVG_MB))
SAFE=$((RAW * <span style="color:#d19a66">80</span> / <span style="color:#d19a66">100</span>))

<span style="color:#c678dd">echo</span> <span style="color:#98c379">&quot;单进程平均内存 : ${AVG_MB} MB&quot;</span>
<span style="color:#c678dd">echo</span> <span style="color:#98c379">&quot;可分配给PHP : ${RESERVE_MB} MB&quot;</span>
<span style="color:#c678dd">echo</span> <span style="color:#98c379">&quot;理论上限 : ${RAW}&quot;</span>
<span style="color:#c678dd">echo</span> <span style="color:#98c379">&quot;建议 max_children: ${SAFE}(已留20%余量)&quot;</span>
<span style="color:#c678dd">echo</span> <span style="color:#98c379">&quot;&quot;</span>
<span style="color:#c678dd">echo</span> <span style="color:#98c379">&quot;配套的 dynamic 配置建议:&quot;</span>
<span style="color:#c678dd">echo</span> <span style="color:#98c379">&quot;pm.max_children = ${SAFE}&quot;</span>
<span style="color:#c678dd">echo</span> <span style="color:#98c379">&quot;pm.start_servers = $((SAFE / 4))&quot;</span>
<span style="color:#c678dd">echo</span> <span style="color:#98c379">&quot;pm.min_spare_servers = $((SAFE / 8))&quot;</span>
<span style="color:#c678dd">echo</span> <span style="color:#98c379">&quot;pm.max_spare_servers = $((SAFE / 3))&quot;</span>
<span style="color:#c678dd">echo</span> <span style="color:#98c379">&quot;pm.max_requests = 500&quot;</span></code></pre>

pm.max_requests 这行别漏。它规定每个子进程处理多少个请求后就自动重启,用来兜底 PHP 代码里的内存泄漏——跑久了进程会越来越胖,漏得慢的时候你可能一两个月都发现不了,等发现时已经吃掉了大半内存。设成 500 左右,进程会被温和地轮换掉,代价几乎可以忽略。

三种进程管理模式的取舍也得说清。static 是一开始就把进程全开好,响应最快但内存常驻占用高;dynamic 按需增减,适合大多数站点;ondemand 用到了才 fork,最省内存但高并发时 fork 本身的开销会拖慢响应。小内存机器上跑访问量不大的站,ondemand 是合理选择;有稳定流量的站用 dynamic 更稳。

改完怎么验?别只看页面能打开。用压测工具按你算出的进程数乘以 1.5 的并发量打一轮,看 FPM 日志里还会不会冒出 max_children,同时盯着内存别让它逼近上限。两项都过关,这个配置才算站得住。

申请创业报道,分享创业好点子。点击此处,共同探讨创业新机遇!

相关标签
网站服务器
php教程

相关文章

  • PHP 500 错误定位四步:日志、开关、堆栈、复现

    500和502的差别,一句话说清:502是Nginx拿不到上游响应,500是PHP自己跑着跑着崩了,然后把这个错误码一路传回浏览器。所以500的现场一定在PHP侧,去Nginx的error.log里翻是找不到的。第一步,确认错误到底出在哪一层。curl拿状态码只是看到表象,真正的判断依据是响应头里有

  • Nginx 504 Gateway Timeout:超时链路上每一环怎么查

    504和502经常被混为一谈,但两者的含义差得远。502是Nginx压根联系不上上游,504是联系上了、请求也发过去了,可上游迟迟不回话,Nginx等不下去了。换句话说,502是后厨关门了,504是后厨开门营业,只是做菜太慢。这个区别决定了排查方向完全不同。502查的是上游在不在,504查的是上游为

    标签:
    网站服务器
  • Nginx 502 Bad Gateway:从 PHP-FPM 到上游的四段排查

    502的全称是BadGateway,翻译过来叫坏网关。这个翻译其实挺误导人的,因为它让人以为网关坏了。真实情况是:Nginx好端端地在那儿,问题永远出在它背后的上游服务,绝大多数时候就是PHP-FPM。搞清这一点,排查方向就不会跑偏。第一段,确认FPM还活着。systemctlstatusphp-f

  • 服务器故障排查总纲:按现象反查的五层定位法

    网站挂了的那一刻,绝大多数人的第一反应是重启。重启Nginx、重启PHP、实在不行重启服务器。这套动作有一半概率管用,但管用的那一次你也不知道为什么管用。下次它还会来,而且大概率挑你最忙的时候来。我后来给自己定了条规矩:不管什么故障,都按固定的五层顺序往下查,每层只用一两条命令确认是或不是,绝不跳层

  • 服务器安全基线审计:Lynis一条命令查出你的薄弱项

    服务器被入侵一次,站长往往才知道安全配置有多重要。问题是Linux安全配置分散在SSH、防火墙、内核参数、文件权限、软件包等几十个地方,人工逐项检查不现实。Lynis就是专门解决这个问题的自动化审计工具。Lynis3.1.7是2026年6月发布的版本,包含448多项检查、39个安全领域,跑完会给出一

    标签:
    网站服务器

热门排行

信息推荐