Skip to content

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 → 150

pm.start_servers(启动进程数)

ini
; dynamic 模式专用
; 值必须大于 min_spare_servers 且小于 max_spare_servers
pm.start_servers = min_spare_servers + (max_spare_servers - min_spare_servers) / 2

pm.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 = 500

pm.status_path(状态页)

ini
; 开启 FPM 状态页
pm.status_path = /fpm-status

; 访问 http://localhost/fpm-status
; 需要 Nginx 配置对应的 location

Nginx 对应配置:

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?html

Prometheus 监控集成

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

最佳实践

  1. 根据内存计算进程数:不要盲目增大 max_children
  2. 开启慢日志:帮助定位性能瓶颈
  3. 设置 max_requests:防止内存泄漏积累
  4. 定期监控状态:关注 listen queue 和 max children reached
  5. 优雅重启:避免使用 restart,使用 reload 实现零停机
  6. 开启 Opcache:极大提升 PHP 执行性能
  7. 分离不同应用的进程池:为不同应用配置独立的 pool

下一节

继续学习:部署流程

参考链接