一、为什么 PHP-FPM 是建站性能的关键
LNMP 架构中,Nginx 负责接收请求与静态资源,PHP-FPM 负责把 PHP 脚本交给 PHP 解释器执行。PHP-FPM 的进程管理策略直接决定站点能扛多少并发、会不会 502、会不会内存被打满。默认配置往往"够用但不高明"——要么进程数过多吃满内存,要么过少导致排队 502。下面从进程管理模式、参数计算、慢日志定位三个层面,把 PHP-FPM 调优讲透。
二、先看懂 pm 三种模式
PHP-FPM 的进程池在 /etc/php/8.x/fpm/pool.d/www.conf(Debian/Ubuntu)或 /etc/php-fpm.d/www.conf(RHEL 系)中配置,核心是 pm 指令:
- static:固定进程数,启动即建好,无伸缩开销,适合流量稳定、内存有富余的场景。
- dynamic:按需在 min/max 之间增减进程,兼顾空闲与峰值,是大多数站点的默认与推荐。
- ondemand:按请求才拉起进程,最省内存,但首次请求有冷启动延迟,适合低流量小站。
# dynamic 模式典型配置 pm = dynamic pm.max_children = 20 # 最大进程数(同时处理请求上限) pm.start_servers = 5 # 启动时进程数 pm.min_spare_servers = 3 # 空闲最少保持 pm.max_spare_servers = 8 # 空闲最多保持 pm.max_requests = 5000 # 每个进程处理 5000 次请求后回收,防内存泄漏累积
max_requests 非常实用:PHP 老代码常有内存泄漏,定期回收进程能避免单进程内存无限膨胀。设置过小会频繁重启进程反而增加开销,一般几千次为宜。
三、max_children 怎么算才不爆内存
核心公式:max_children ≈ 可用内存 ÷ 单进程平均内存。先看每个 PHP 进程实际吃多少内存:
# 当前 php-fpm 进程数与内存占用
ps -eo rss,cmd | grep php-fpm | grep -v grep | awk '{sum+=$1;n++} END {print "进程数:",n," 平均RSS(MB):",sum/n/1024}'
假设单进程平均 RSS 40MB、机器可用内存 2GB、系统与 Nginx/MySQL 预留 1GB,则:max_children = (2-1)GB / 40MB ≈ 25。
设得过大:高并发下内存被 PHP 进程吃满,触发 OOM killer 连 MySQL 一起杀,表现为整机卡死;设得过小:请求排队,出现 502/504。调参要留足系统与数据库余量,尤其数据库连接数也随并发上涨,别把内存全部分给 PHP。
四、必开慢日志:让瓶颈无所遁形
站点变慢最怕瞎猜。PHP-FPM 的慢日志能精准记录"哪个脚本执行超过多少秒",是定位慢接口的利器:
# www.conf 中开启 slowlog = /var/log/php-fpm-slow.log request_slowlog_timeout = 3s # 超过 3 秒的执行计入慢日志 request_terminate_timeout = 60s # 单请求最长执行 60 秒,超时强制结束(防死循环拖垮进程)
查看慢日志:
tail -n 100 /var/log/php-fpm-slow.log # 典型输出会给出脚本路径、请求开始时间,以及当时的 PHP 调用栈(哪一行卡住)
慢日志配合前面讲过的 Nginx access log 与 MySQL 慢查询,基本能锁定"是代码问题、SQL 问题还是配置问题"。注意 request_terminate_timeout 设置过小会误杀正常的长任务(如导出、批量处理),要按业务实际评估。
五、socket 与 listen 队列调优
PHP-FPM 默认监听 Unix socket,避免 TCP 开销,但 socket 的队列长度不足会丢请求导致 502。相关调优点:
# php-fpm www.conf listen = /run/php/php8.2-fpm.sock listen.backlog = 511 # 连接等待队列长度,大并发可调大到 1024 # listen.allowed_clients = 127.0.0.1 # TCP 模式时限制来源 # Nginx 侧 fastcgi 超时与缓冲(nginx.conf 中 location ~ \.php$) fastcgi_connect_timeout 5s; fastcgi_read_timeout 60s; # 与 request_terminate_timeout 匹配 fastcgi_send_timeout 60s;
如果看到 "connect() to unix:/run/php/...sock failed (11: Resource temporarily unavailable)",多半就是 socket 队列被塞满——要么调大 backlog,要么提升 max_children,要么检查是否有慢脚本把进程占满了。
六、与 Nginx 和 MySQL 的联动调优
PHP-FPM 不是孤立的,链路任一环节都可能成为瓶颈:
# 观察 Nginx 上游与 PHP-FPM 连接状态 tail -f /var/log/nginx/error.log | grep -E 'upstream|connect|timed out' # 关键联动检查项 1. Nginx worker 数是否足够(worker_processes auto) 2. keepalive 是否开启,避免频繁建连 3. MySQL max_connections 是否高于 PHP 并发请求数(否则 SQL 排队) 4. 是否配了 opcache(PHP 7+ 默认),并关注 opcache 内存是否够
opcache 是"免费的性能大礼包":它把编译后的字节码缓存下来,PHP 不再每次重复编译。检查与调优:
php -i | grep opcache.enable_cli # opcache.enable=1 已开启,生产建议 opcache.memory_consumption=128M、validate_timestamps 视部署方式而定
七、常见故障速查表
502 Bad Gateway -> php-fpm 进程数打满 / socket 权限不对 / 进程崩溃,查 error.log 与 php-fpm 状态 504 Gateway Timeout -> fastcgi_read_timeout 太短,或脚本执行超时,调大超时并查慢日志 503 Service Unavailable -> 站点负载过高排队,或 php-fpm 未启动(systemctl status php8.2-fpm) 整机卡死/OOM -> max_children 过大吃满内存,按公式回调并加 Swap
快速看 php-fpm 实时状态:开启 pm.status_path = /status,配到 Nginx 后访问 https://site/status 能看 active 进程数、队列等指标,是判断"进程够不够"的直接依据。
写在最后
PHP-FPM 调优的要点可归纳为:选择合适 pm 模式、按内存公式算 max_children、开启慢日志定位真凶、调好 socket 与超时、联动 Nginx/MySQL/opcache 整体优化。切忌盲目抄网上"max_children=100"之类的配置,每台机器的内存与业务都不同,用状态页 + 慢日志 + ps 实测来定参数,才能既扛得住峰值、又不把内存吃爆。




