PHP-FPM 调优
PHP-FPM(FastCGI Process Manager)是 PHP 最常用的进程管理器,负责管理 PHP 进程池、处理来自 Web 服务器的请求。合理的 PHP-FPM 配置直接关系到应用的并发处理能力、内存使用效率和响应时间。本节将详细介绍 PHP-FPM 的进程管理模式、关键参数调优、慢日志分析以及性能监控方法。
前置知识
阅读本节前,建议先了解:Nginx + PHP-FPM 配置、Docker 部署
基础概念
PHP-FPM 架构
text
┌─────────────────┐
│ Nginx/Apache │
│ (Web Server) │
└────────┬────────┘
│ FastCGI Protocol
┌────────┴────────┐
│ PHP-FPM Master│
│ (Process Mgr) │
└────────┬────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
│ Worker 1 │ │ Worker 2 │ │ Worker N │
│ (Pool) │ │ (Pool) │ │ (Pool) │
└───────────┘ └───────────┘ └───────────┘核心配置文件
| 文件 | 说明 |
|---|---|
php-fpm.conf | 全局配置 |
php-fpm.d/www.conf | 进程池配置(通常命名为 www) |
php-fpm.d/www.conf.default | 默认配置模板 |
进程管理模式
static(静态模式)
ini
; 所有子进程在启动时预创建,不动态调整
pm = static
; 固定数量的子进程
pm.max_children = 50静态模式的特点:
- 启动时立即创建所有进程,内存占用恒定
- 无创建/销毁进程的开销
- 适合流量稳定、可预测的场景
- 如果进程不够用,请求会排队等待
dynamic(动态模式)
ini
pm = dynamic
; 最大子进程数
pm.max_children = 50
; 启动时创建的进程数
pm.start_servers = 5
; 空闲时的最小进程数
pm.min_spare_servers = 5
; 空闲时的最大进程数
pm.max_spare_servers = 20动态模式的特点:
- 根据负载动态调整进程数量
- 流量低时减少进程,节省内存
- 流量高时创建新进程
- 有进程创建/销毁的开销
ondemand(按需模式)
ini
pm = ondemand
; 最大子进程数(当没有空闲进程时创建)
pm.max_children = 50
; 空闲进程超时时间(秒),超时后销毁
pm.process_idle_timeout = 10按需模式的特点:
- 仅在有请求时才创建进程
- 空闲超时后销毁进程
- 内存使用最优化
- 进程创建有延迟,不适合高并发低延迟场景
模式选择建议
| 场景 | 推荐模式 | 说明 |
|---|---|---|
| 高流量生产环境 | static | 稳定可预测,无进程创建开销 |
| 流量波动的应用 | dynamic | 自动伸缩,节省资源 |
| 开发/测试环境 | ondemand | 节省资源 |
| 低流量 + 偶尔突发 | dynamic | 在响应速度和资源间平衡 |
| 内存受限环境 | ondemand | 最小化内存占用 |
核心参数详解
pm.max_children(最大进程数)
这是 PHP-FPM 最重要的参数,决定了最大并发处理能力:
ini
pm.max_children = 50计算公式:
text
max_children = (可用总内存 - 系统保留内存) / 每个PHP进程平均内存bash
# 查看每个 PHP-FPM 进程的内存占用
ps -ylC php-fpm --sort:rss | head -20
# 或使用
ps aux | grep php-fpm | grep pool
# 计算示例
# 服务器总内存:16 GB
# 系统保留:4 GB(OS + Nginx + 其他服务)
# 每个进程平均内存:80 MB
# max_children = (16 - 4) * 1024 / 80 = 153.6 → 150pm.start_servers(启动进程数)
ini
; dynamic 模式专用
; 值必须大于 min_spare_servers 且小于 max_spare_servers
pm.start_servers = min_spare_servers + (max_spare_servers - min_spare_servers) / 2pm.min_spare_servers / pm.max_spare_servers
ini
pm.min_spare_servers = 5
pm.max_spare_servers = 20
; min_spare_servers: 保证至少有这么多个空闲进程
; max_spare_servers: 空闲进程超过此数量则销毁多余进程pm.max_requests(最大请求数)
ini
; 每个子进程处理的最大请求数,达到后重启该进程
; 设置为 0 表示不限制
; 建议设置为 500-1000,防止内存泄漏
pm.max_requests = 500pm.status_path(状态页)
ini
; 开启 FPM 状态页
pm.status_path = /fpm-status
; 访问 http://localhost/fpm-status
; 需要 Nginx 配置对应的 locationNginx 对应配置:
nginx
location /fpm-status {
allow 127.0.0.1;
allow 10.0.0.0/8;
deny all;
fastcgi_pass unix:/var/run/php-fpm/www.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}状态页输出示例:
text
pool: www
process manager: dynamic
start time: 15/Jan/2024:10:00:00 +0800
start since: 86400
accepted conn: 125000
listen queue: 0
max listen queue: 15
listen queue len: 128
idle processes: 8
active processes: 12
total processes: 20
max active processes: 45
max children reached: 0
slow requests: 0完整配置示例
高流量生产环境(16GB 内存)
ini
[www]
; 进程管理模式
pm = dynamic
; 最大子进程数
pm.max_children = 150
; 启动进程数
pm.start_servers = 20
; 最小空闲进程
pm.min_spare_servers = 15
; 最大空闲进程
pm.max_spare_servers = 35
; 每个进程最大请求数
pm.max_requests = 500
; 状态页
pm.status_path = /fpm-status
; 状态页输出格式
pm.status_listen = 127.0.0.1:9001
; 慢日志
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slowlog-www.log
; 超时设置
request_terminate_timeout = 60s
; 进程优先级
process.priority = -5
; 优雅重启超时
process_control_timeout = 10s
; 环境变量
env[APP_ENV] = production
env[APP_DEBUG] = false
; PHP 值覆盖
php_admin_value[open_basedir] = /var/www/app:/tmp:/usr/share/php
php_admin_value[upload_max_filesize] = 50M
php_admin_value[post_max_size] = 55M
php_admin_value[memory_limit] = 256M
php_admin_value[max_execution_time] = 60
php_admin_value[max_input_time] = 60
php_admin_value[session.save_handler] = redis
php_admin_value[session.save_path] = "tcp://127.0.0.1:6379"
php_value[display_errors] = Off
php_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT
php_value[date.timezone] = Asia/Shanghai
; Opcache 配置
php_admin_value[opcache.enable] = 1
php_admin_value[opcache.enable_cli] = 0
php_admin_value[opcache.memory_consumption] = 256
php_admin_value[opcache.interned_strings_buffer] = 16
php_admin_value[opcache.max_accelerated_files] = 40000
php_admin_value[opcache.validate_timestamps] = 0
php_admin_value[opcache.revalidate_freq] = 0
php_admin_value[opcache.save_comments] = 1
php_admin_value[opcache.jit] = 1255
php_admin_value[opcache.jit_buffer_size] = 128M低内存环境(2GB 内存)
ini
[www]
pm = ondemand
pm.max_children = 20
pm.process_idle_timeout = 10s
pm.max_requests = 200
request_terminate_timeout = 30s
request_slowlog_timeout = 3s
slowlog = /var/log/php-fpm/slowlog-www.log
php_admin_value[memory_limit] = 128M
php_admin_value[opcache.memory_consumption] = 64
php_admin_value[opcache.max_accelerated_files] = 10000慢日志配置
ini
; 记录执行时间超过 5 秒的请求
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slowlog-www.log
; 记录执行时间超过 2 秒的请求的完整 backtrace
request_slowlog_timeout = 2s
request_slowlog_trace_depth = 20
slowlog = /var/log/php-fpm/slowlog-www.log慢日志输出示例:
text
[15-Jan-2024 10:30:45] WARNING: [pool www] child 1234, script
/var/www/app/public/index.php - request_slowlog_timeout (5s)
[15-Jan-2024 10:30:45] TRACE: [pool www] child 1234 -
Executing /var/www/app/public/index.php
[0x00007f8a12345678] strlen() /var/www/app/vendor/some/package/src/Function.php:123
[0x00007f8a12345679] processRequest() /var/www/app/src/Service/ApiService.php:456
[0x00007f8a12345680] handle() /var/www/app/src/Http/Controller/ApiController.php:78
[0x00007f8a12345681] {main}() /var/www/app/public/index.php:30优雅重启与信号管理
bash
# 优雅重启(完成当前请求后再重启)
sudo systemctl reload php-fpm
# 或
kill -USR2 $(cat /var/run/php-fpm/php-fpm.pid)
# 或
sudo kill -USR2 $(pgrep -f "php-fpm: master")
# 强制重启
sudo systemctl restart php-fpm
# 优雅停止
sudo systemctl stop php-fpm
# 或
kill -QUIT $(cat /var/run/php-fpm/php-fpm.pid)
# 信号说明
# SIGINT, SIGTERM (立即关闭)
# SIGQUIT (优雅关闭)
# SIGUSR1 (重新打开日志文件)
# SIGUSR2 (优雅重启)性能监控
通过状态页监控
bash
# 获取 JSON 格式状态
curl http://127.0.0.1:9001/fpm-status?json
# 获取完整状态(含每个进程信息)
curl http://127.0.0.1:9001/fpm-status?full
# 获取 HTML 格式
curl http://127.0.0.1:9001/fpm-status?htmlPrometheus 监控集成
php
<?php
declare(strict_types=1);
// fpm-exporter 或自定义导出器
// 使用 php-fpm 状态页采集指标
$fpmStatus = file_get_contents('http://127.0.0.1:9001/fpm-status?json');
$data = json_decode($fpmStatus, true);
// 导出 Prometheus 指标
echo sprintf(
"php_fpm_accepted_connections_total %d\n",
$data['accepted conn']
);
echo sprintf(
"php_fpm_active_processes %d\n",
$data['active processes']
);
echo sprintf(
"php_fpm_idle_processes %d\n",
$data['idle processes']
);
echo sprintf(
"php_fpm_listen_queue %d\n",
$data['listen queue']
);实战示例
根据服务器配置计算参数
bash
#!/bin/bash
# PHP-FPM 配置计算器
TOTAL_MEM=$(grep MemTotal /proc/meminfo | awk '{print $2}') # KB
TOTAL_MEM_MB=$((TOTAL_MEM / 1024))
# 系统保留(GB)
SYS_RESERVED=4096
# 每个进程平均内存(MB)
PROC_MEM=80
# 计算 max_children
AVAILABLE=$(( (TOTAL_MEM - SYS_RESERVED) / 1024 ))
MAX_CHILDREN=$((AVAILABLE / PROC_MEM))
echo "总内存: ${TOTAL_MEM_MB}MB"
echo "可用内存: ${AVAILABLE}MB"
echo "推荐 max_children: ${MAX_CHILDREN}"
echo "推荐 start_servers: $((MAX_CHILDREN / 10))"
echo "推荐 min_spare_servers: $((MAX_CHILDREN / 10))"
echo "推荐 max_spare_servers: $((MAX_CHILDREN / 3))"注意事项
常见调优错误
max_children设置过大导致内存溢出(OOM Killer 会杀死进程)request_terminate_timeout设置过小导致长任务被中断- 未开启慢日志导致性能问题难以排查
- 使用 ondemand 模式但未配置合理的
process_idle_timeout
最佳实践
- 根据内存计算进程数:不要盲目增大
max_children - 开启慢日志:帮助定位性能瓶颈
- 设置
max_requests:防止内存泄漏积累 - 定期监控状态:关注 listen queue 和 max children reached
- 优雅重启:避免使用
restart,使用reload实现零停机 - 开启 Opcache:极大提升 PHP 执行性能
- 分离不同应用的进程池:为不同应用配置独立的 pool
下一节
继续学习:部署流程