前言:为什么你的 PHP 网站越跑越慢
很多个人站长用的是 LNMP 一键包或者宝塔面板搭建的网站,平时只关注 Nginx 和数据库,却忽略了最关键的 PHP-FPM。网站访问变慢、偶尔 502、高峰期直接打不开,十有八九问题就出在 PHP-FPM 的默认配置上。默认配置是为通用场景准备的,不会照顾你那台只有 1G 内存的小 VPS。本文从进程模型讲起,带你一步步把 PHP-FPM 调到适合自己站点的状态,并学会用慢日志精准定位卡顿的脚本。
一、理解 PHP-FPM 的进程模型
PHP-FPM 是 PHP 的 FastCGI 进程管理器,它由一个 master 主进程和若干个 worker 工作进程组成。master 进程负责监听端口、管理 worker 的创建与回收,真正执行 PHP 代码的是 worker 进程。每次有请求进来,Nginx 会通过 FastCGI 协议把请求交给一个空闲的 worker 处理,处理完再还给 Nginx,worker 继续等待下一个请求。
这个模型的关键在于:worker 进程是宝贵的资源,每个 worker 同一时间只能处理一个请求。如果并发请求数超过了 worker 总数,多余的请求就得排队等待,表现为 TTFB 越来越长;排队超过一定时间,Nginx 就会返回 504 Gateway Timeout;如果 worker 全部卡死,就直接 502 Bad Gateway。所以调优的核心,就是让 worker 数量与服务器的内存、流量匹配。
二、pm 三种模式怎么选
PHP-FPM 的进程管理模式由 pm 指令决定,共三种:static、dynamic、ondemand。理解它们的区别是调优的第一步。
| 模式 | worker 数量 | 适用场景 |
|---|---|---|
| static | 固定不变 | 流量稳定、内存充足的服务器 |
| dynamic | 在 min 和 max 之间浮动 | 大多数个人网站,推荐 |
| ondemand | 按需启动,空闲回收 | 内存极小、访问量很低的站 |
static 模式启动时一次性创建固定数量的 worker,响应最快,没有创建进程的开销,但如果设置太多会白白浪费内存,设置太少高峰期又扛不住。dynamic 模式会按照 pm.start_servers、pm.min_spare_servers、pm.max_spare_servers 三个参数动态调整,兼顾了响应速度和资源占用,是个人站长最常用的选择。ondemand 模式平时只有少量 worker,来请求才临时拉起,适合那种一天没几个访客的极小站点,能省下大量内存。
三、pm.max_children 到底该设多少
pm.max_children 是所有配置里最核心的一项,它决定了最多能同时处理多少个请求。设少了高峰期排队,设多了内存不够直接 OOM。正确的做法是用内存来反推:先统计每个 PHP-FPM worker 平均占用多少内存,再用服务器可用内存除以单进程占用。
统计 worker 内存占用,一条命令搞定:
ps -ylC php-fpm --sort:rss | awk '{print $8}' | tail -n +2 | awk '{sum+=$1; n++} END {printf "平均每个 worker 占用: %.1f MB,共 %d 个 worker\n", sum/n/1024, n}'假设统计出每个 worker 平均占用 60MB,服务器可用内存是 1G,那么 max_children 可以设为 1024 除以 60,约等于 15,留一点余量就设 12 到 14。注意这里说的是可用内存,不是总内存,要把 Nginx、MySQL 占用的都扣掉。MySQL 如果没优化过,随便吃两三百兆很正常。
除了内存,还要看 CPU 核数。单核 CPU 的机器,worker 设再多,CPU 也就一个,超出的 worker 只会排队抢 CPU。一般来说 max_children 设置在 CPU 核数的 5 到 10 倍比较合理,再结合内存限制取较小值。
四、几个容易被忽略的关键参数
除了 max_children,下面这几个参数对稳定性和排障同样重要。
pm.max_requests:每个 worker 处理完多少次请求后自动重启。这个参数非常有用,因为 PHP 代码里的内存泄漏(比如某个扩展没释放内存)会随着请求次数累积,worker 内存越涨越高。设置 pm.max_requests = 500 或者 1000,让 worker 定期轮换,内存就能保持稳定。很多站长遇到"网站跑几天就变慢,重启 PHP-FPM 就好了"的问题,就是缺了这个参数。
request_terminate_timeout:单个请求的最长执行时间,超过就强制杀掉。默认值是 0 表示不限制,这很危险——如果某个脚本死循环或者调用了外部接口一直不返回,worker 会被永久占用。建议设置为 60 秒,配合下面的慢日志使用。
request_slowlog_timeout 和 slowlog:这是定位卡顿脚本的利器。设置 request_slowlog_timeout = 5 并指定 slowlog 路径,任何执行超过 5 秒的请求都会把调用堆栈写进慢日志,你就能看到到底是哪个函数在拖时间。
listen.backlog:请求队列的长度。并发高的时候,来不及处理的请求会进队列,队列满了 Nginx 就会报连接被拒绝。默认 128 在流量大的时候不够,可以提高到 1024,同时要注意系统层面的 somaxconn 也要跟着调大:
echo 1024 > /proc/sys/net/core/somaxconn五、慢日志实战:找出拖慢网站的元凶
配置好慢日志之后,过一两天就能积累一批数据。查看慢日志:
tail -n 50 /var/log/php-fpm-slow.log慢日志里每一段记录一个慢请求,包含请求的 URL 和执行时间,最关键的是底部的函数调用栈,从下往上看就能找到耗时最多的函数。常见的元凶有这几类:
- 数据库查询没走索引,一条 SQL 扫全表,几百万行数据查好几秒。
- 循环里重复查库,比如在 for 循环里执行 select,N 次循环就是 N 次查询。
- 调用外部 API 超时,比如支付回调、第三方登录,对方响应慢就把你的 worker 拖住。
- 图片处理函数,比如用 GD 库压缩大图,非常吃 CPU 和内存。
定位到具体函数后,优化方向就很明确了:SQL 加索引、循环外提查询、外部调用加超时、图片处理改成异步队列。慢日志的价值在于,它把"网站慢"这个模糊的问题,变成了"哪个脚本的哪个函数慢"这个精确的问题。
六、配合 php.ini 和 opcache 一起调
PHP-FPM 调优不能只盯着进程池,php.ini 里的两个参数直接影响性能和稳定性。
memory_limit:单个 PHP 请求最多能用多少内存。默认 128M 对大多数站点够用,但如果你装了比较吃内存的插件或者主题,可以适当提高到 256M。注意这个值不能超过 worker 可用的内存,否则一个请求就把内存吃光了。每个 worker 占用 60MB 是包含这个限制在内的平均值。
opcache:PHP 的字节码缓存。不开 opcache 的话,每个请求都要重新解析一遍所有 PHP 文件,CPU 开销巨大。在 php.ini 里确认:
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60开了 opcache 之后,同样的硬件条件下网站吞吐量能提升 30% 到 50%,这是性价比最高的优化,没有之一。改完配置记得重启 PHP-FPM 才能生效:
systemctl reload php-fpm七、常见故障速查
最后整理几个高频问题的排查思路,供大家遇到问题时快速对照。
- 频繁 502:worker 全部卡死或崩溃,先看 php-fpm 日志,多半是内存不足或代码 fatal error。
- 高峰期 504:request_terminate_timeout 超时,或者 max_children 设置太小导致排队。
- worker 内存持续上涨:检查 pm.max_requests 是否设置,设置后能自动回收泄漏的内存。
- 改完配置不生效:确认改的是不是 php-fpm 使用的那个 php.ini,用 php --ini 查看实际加载路径。
八、用状态页实时监控 worker 运行情况
PHP-FPM 自带一个状态页,可以看到当前活跃连接数、空闲进程数、请求队列长度等关键指标,排查问题非常直观。先在 pool 配置里开启状态路径:
pm.status_path = /status然后在 Nginx 的站点配置里加一个 location,把状态页请求转发给 PHP-FPM:
location ~ ^/status$ {
fastcgi_pass unix:/run/php/php-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}重启 PHP-FPM 和 Nginx 后,访问 https://你的域名/status 就能看到实时数据。重点看 listen queue 这一项:如果排队数长期大于 0,说明 worker 不够用,需要调大 max_children;如果空闲进程一大片,说明设置偏多,可以适当调小节省内存。这个页面建议在 location 里加 allow 和 deny 指令,只允许你自己的 IP 访问,避免被陌生人窥探服务器信息。
九、调优的节奏与注意事项
最后提醒几点:第一,修改配置前先备份原文件,改坏了能快速回滚;第二,一次只改一个参数,观察一两天再动下一个,方便判断每个改动到底起了什么作用;第三,调优要在访问高峰期之后看效果,用监控工具记录调整前后的响应时间对比,而不是凭感觉;第四,服务器如果开了 Swap,内存统计要把 Swap 也算进去,避免 max_children 设置得过于激进导致频繁换页,反而拖慢速度。
总结
PHP-FPM 调优不是玄学,核心就三步:算清楚 worker 数量、设好 max_requests 防止内存泄漏、开慢日志定位慢脚本。把这三点做好,你的网站能扛住的并发和响应速度都会有明显提升。调优是一个持续的过程,建议每改一个参数就观察几天,结合访问量和内存曲线微调,而不是一次把参数拉到极限。先按本文的方法把基础打牢,再根据自己站点的实际情况做精细调整,你的小 VPS 也能跑得又快又稳。