先算清单个进程吃多少内存,max_children 才有依据
调 PHP-FPM 最容易踩的坑,是照着网上流传的「max_children 设 50」「设 100」直接抄。同一份配置,在 2G 内存的机器上会把系统压到 OOM,在 32G 的机器上又白白浪费资源。真正该做的第一步,是测出单个 php-fpm 进程的常驻内存。方式很简单:服务跑一段真实流量后,用 ps 或 top 看每个 worker 的 RSS,取一个偏高值作为基准,比如 60MB 到 80MB。注意要按业务最重的页面估算,别拿首页静态缓存来算,否则算出来的数字会偏乐观。
有了单进程内存,再列出服务器上其他常驻服务的内存占用:MySQL、Redis、Nginx、日志与系统本身。假设一台 8G 的机器,系统加数据库预留 3G,剩下 5G 给 PHP,单进程按 64MB 算,理论上限约 80 个,实际配置取八成即 64 个左右,留出余量应对突发和内存碎片。这个数字是上限,不是目标值,后续的 pm 模式与空闲进程数都要围绕它展开。
选对 pm 模式,别一上来就 dynamic
PHP-FPM 提供三种进程管理方式,选错模式比参数写错更伤。
- static:启动时就把固定数量的 worker 常驻内存,没有 fork 开销,响应最稳、延迟最低,适合内存充足且流量平稳的服务,比如专用 API 集群。缺点是空闲时也占着内存不放。
- dynamic:根据负载在 min 和 max 之间伸缩,是绝大多数站点的默认选择。它会 fork 新进程,高并发瞬间存在一定开销,配置不当时会出现频繁创建销毁的抖动。
- ondemand:没有请求就不启动 worker,适合 1 核 1G、2G 这类小内存机器,或访问稀疏的后台服务。代价是首个请求要等进程创建,延迟略高。
判断标准很直接:内存宽裕、流量稳定选 static;流量有波峰波谷、内存中等选 dynamic;内存紧张、访问量小选 ondemand。不要为了「看起来灵活」在低配机器上强行 dynamic 并把 max_children 拉满,那是内存告急的常见来源。
dynamic 模式下的四个数字要成套计算
dynamic 模式的关键参数是 start_servers、min_spare_servers、max_spare_servers 和 max_children。它们的约束关系是硬性的:min_spare_servers 必须小于等于 start_servers,max_spare_servers 必须大于等于 min_spare_servers,否则 PHP-FPM 启动时直接报错,这是新手最常撞的墙。
取值上有一个稳妥的比例。start_servers 取 max_children 的 10% 到 20%,min_spare_servers 与 start_servers 持平,max_spare_servers 取 max_children 的 30% 到 40%。以 max_children 为 64 为例,start_servers 和 min_spare_servers 设 8 到 12,max_spare_servers 设 24 左右。这样既保证有足够空闲进程接住突发请求,又不至于让一堆空闲 worker 白占内存。空闲进程太少,请求会排队;空闲进程太多,内存就被闲置进程吃掉,两者都要靠监控实际调。
用超时、慢日志和进程回收兜住异常
参数配好不代表就稳了,业务里一个慢查询或第三方接口挂起,就能把 worker 全部占死。request_terminate_timeout 要设成略大于业务最长正常执行时间,比如 60 秒,超过就强制结束,避免进程无限挂起;同时它要和 Nginx 的 fastcgi_read_timeout 配合,前者应不大于后者,否则会出现 Nginx 先断开、PHP 还在傻跑的情况。
request_slowlog_timeout 建议设 5 秒并开启 slowlog,把执行慢的调用栈记下来,这是定位性能问题最省事的手段。pm.max_requests 设 500 到 1000,让 worker 处理一定请求数后主动退出重建,能有效缓解某些扩展或业务代码的内存泄漏,代价只是偶尔一次进程重建的开销。此外,emergency_restart_threshold 搭配 emergency_restart_interval 可以在大量子进程异常退出时自动重启 master,process_control_timeout 给进程退出留出缓冲,避免平滑重载时请求被硬切。
最后几个细节和验证方法
把 listen 改成 Unix socket 并放在 /dev/shm 或 /tmp 下,比走 127.0.0.1 的 TCP 少一层协议栈开销,同时把 listen.backlog 调到 511 或更高,应对瞬时排队。记得开启 status 页面,观察 active processes、max children reached 和 listen queue 三个指标:max children reached 持续增长说明 max_children 偏小或存在慢请求堵塞,listen queue 长期非零说明队列在堆积。压测时用 ab 或 wrk 打一轮,同时盯住 free 内存和 swap,只要 swap 开始被使用,就说明参数已经越过安全线。参数没有一劳永逸的答案,按机器配置算出上限,再用监控在真实流量下微调,才是稳的做法。