
本文教你如何在一台四核八 GB 内存的 Linux 服务器上,把 PHP-FPM 与 OPcache 系统地调到一个合理的工作点,让 WordPress 或自研 PHP 应用在面对突发流量时不再频繁返回 502,也不会因为进程数失控直接被系统的内存溢出守护进程干掉。为什么这两个组件需要一起调而不能分开看?因为 PHP-FPM 决定的是后端到底有几个进程在并行处理请求,OPcache 决定的是每个进程在干活时还要不要重新解析和编译 PHP 源文件,两者的内存账目必须合起来算才不会在峰值时撞墙。读完本文你会拿到三份可以直接落地的产出:一份按物理内存反推进程数的计算模板、一组同时适配 WordPress 站点与 API 型负载的参数对照表,以及一套上线后用压测工具与状态接口做命中率复盘的标准流程。本文示例统一以 PHP 8.2、Nginx 与 Debian 12 为基础环境。
一、为什么不能照搬别人贴的配置
社区里到处都能看到现成的 PHP-FPM 配置片段,但它们最大的问题是从来不交代服务器规格与业务画像。一份在十六 GB 机器上跑得很舒服的 pm.max_children = 50,搬到只有四 GB 内存的小机器上跑 WordPress,几乎一定会在第一次流量小高峰时把内存吃光,然后被内核里的内存溢出守护进程一刀干掉。
调参之前你必须先回答清楚三个问题:单个 PHP 进程在你这套代码上的稳态内存占用是多少?你能给 PHP-FPM 这个组件实际预留多少物理内存?你的应用整体上是计算密集型还是 IO 密集型?
第一个问题用 ps -ylC php-fpm8.2 –sort:rss 看 RSS 列,跑半小时压测后取百分之九十五分位。WordPress 加上常用插件后单进程占用通常在八十到一百二十 MB 之间,纯 API 型的应用一般在四十到八十 MB 之间。
第二个问题要从机器总内存里依次扣掉:操作系统大约五百 MB,Nginx 大约两百 MB,MySQL 根据配置在一到四 GB,Redis 根据使用情况在两百 MB 到两 GB,剩下的才是真正可以给 PHP-FPM 的预算。
第三个问题决定进程管理模型的选择:偏计算密集型选 static,偏 IO 密集型选 dynamic 或 ondemand。
二、按内存反推进程数的计算模板
公式很简单:pm.max_children = (可用内存 – 安全余量) / 单进程平均内存。
举例:8GB 服务器,扣掉系统、Nginx、MySQL(按 2GB 估)、Redis(按 512MB 估)后还剩约 4.5GB,留 500MB 安全余量,可用 4GB;WordPress 单进程按 100MB 算,pm.max_children 应配在 40 左右。
下面是三档常见配置的参考值:
- 2GB 机器,WordPress 小站:pm = ondemand,max_children = 8,process_idle_timeout = 10s
- 4GB 机器,WordPress 中等流量:pm = dynamic,max_children = 20,start_servers = 4,min_spare = 2,max_spare = 8
- 8GB 机器,API 型高并发:pm = static,max_children = 40,max_requests = 1000
pm.max_requests 是个容易被忽略的参数,它控制一个 worker 处理多少请求后销毁重建。WordPress 这种带 PHP 扩展和插件的环境,长期运行容易内存泄漏,建议设 500–1000;纯 API 服务可以设到 5000 或更高。
把 pm 设成 static 时不要忘了 max_children 就是常驻进程数,内存占用是恒定的;dynamic 是按需扩缩,max_spare_servers 是上限;ondemand 最省内存,但首请求有冷启动延迟,不适合 SLA 敏感的接口。
三、OPcache 的内存账与命中率
OPcache 把 PHP 字节码缓存在共享内存里,让每次请求免去解析与编译。不开 OPcache 的 PHP-FPM 性能直接砍半,所以这一节是必修。
核心参数与推荐值:
- opcache.memory_consumption = 256(小站 128,大站 512)
- opcache.interned_strings_buffer = 16
- opcache.max_accelerated_files = 20000(WordPress 加多插件容易超 10000)
- opcache.revalidate_freq = 60(生产)/ 0(开发)
- opcache.validate_timestamps = 1(生产可关,但需要 reload 流程配合)
- opcache.fast_shutdown = 1
- opcache.enable_file_override = 1
max_accelerated_files 设小了会把热文件不停换出,命中率掉得厉害。WordPress 站点上线后用 opcache_get_status() 的 num_cached_scripts 字段除以 max_accelerated_files,比例应低于 75%,否则要扩容。
memory_consumption 也要看 used_memory 与 free_memory 的比例。如果 free_memory 长期低于 10%,说明配小了,会触发频繁的内存清理,命中率断崖式下跌。
把 validate_timestamps 关掉是生产环境最大的性能收益项,关掉后每次部署必须 systemctl reload php8.2-fpm,建议在 CI/CD 里写死这一步。
四、Nginx 端的协同配置
Nginx 与 PHP-FPM 之间走 unix socket 通常比 TCP 快 10% 左右。但 socket 文件的权限要给对,否则 Nginx 会报 connect() to unix:/run/php-fpm.sock failed (13: Permission denied)。
关键参数在 nginx.conf 的 location ~ .php$ 块:fastcgi_read_timeout 设 60s(API 长任务设更高),fastcgi_buffer_size 设 16k,fastcgi_buffers 设 8 16k。这套参数对 WordPress 后台保存大文章很关键,否则 502。
如果前置了反向代理或负载均衡分发到多台 PHP-FPM 节点,可以参考 Nginx 反向代理与负载均衡配置 里 upstream 段的健康检查与 keepalive 配置,能显著降低 PHP-FPM 节点的连接抖动。
WordPress 站点同时启用了 W3 Total Cache 时,PHP-FPM 的命中率会被 page cache 拉低(因为很多请求根本不到 PHP 层),这是预期行为,不是异常。完整的缓存层与 OPcache 协作策略可以看 WordPress W3 Total Cache 调优实战。
五、上线后的复盘 SOP
调完参数后必须做一次压测复盘。先在 pool 配置里加 pm.status_path = /fpm-status 暴露状态接口,Nginx 端用 allow 127.0.0.1 限制访问。然后用 ab 或 wrk 跑两轮:正常并发 50 跑 5 分钟看 max children reached 是否大于 0,极限并发 100 看 listen queue 是否堆积;前者命中说明进程数配小,后者命中要考虑加 listen.backlog 或扩容。
OPcache 命中率看 opcache_get_status()[‘opcache_statistics’][‘opcache_hit_rate’],生产环境应高于 99%;低于这个值大概率是 max_accelerated_files 或 memory_consumption 配小了。同时把 slowlog 阈值设 5s,跑一段时间复盘反复出现的文件,那基本就是真正的性能瓶颈。
部署在云 VPS 上的服务,IO 瓶颈往往先于 CPU 暴露,磁盘选型与系统参数可以参考 美国 VPS 部署完整教程。前置 CDN 后 PHP-FPM 真实压力会下来一个数量级,CDN 接入看 Cloudflare CDN 中国大陆访问优化;SSH 加固可同步参考 Linux SSH 与 Fail2ban 加固。
六、总结与下一步
PHP-FPM 与 OPcache 调优的核心不是抄一份配置,而是先算清楚内存账、再按业务画像选 pm 模型、最后用 status 接口与压测验证。建议把调优后的参数写进 Ansible 或同等的配置管理工具,保证下次扩容时新机器一上来就是工作点。如果你需要更细粒度的观测,可以接入 New Relic 或 Datadog 的 PHP agent,能直接看到每个 endpoint 的 P95 与 OPcache 命中率。综合来说,按本文的计算模板与对照表先把基础值定下来,再用复盘 SOP 验证一遍,绝大多数 PHP 站点的性能问题就能收敛。Hostease 的托管 VPS 客户可在工单中申请预装好的 PHP-FPM 调优模板。