编程 PHP-FPM 生产调优:OPcache/JIT、max_children 计算与 Composer classmap 的坑

2026-09-15 21:32:09

PHP-FPM 生产调优:OPcache/JIT、max_children 计算与 Composer classmap 的坑

OPcache 把预编译后的 opcode 缓存到共享内存,跳过词法、语法分析与编译阶段,降低 CPU 占用、缩短响应时间。

opcache.enable=1
; CLI 环境需要单独打开
opcache.enable_cli=1

OPcache 参数

opcache.memory_consumption:中小应用 128–256MB,大型应用 512MB 或更高。监控时让使用率维持在 75%–90%,长期贴近 100% 说明该扩了。

opcache.interned_strings_buffer:8–64MB,大型框架从 16/32MB 起。

opcache.max_accelerated_files:必须大于项目 PHP 文件总数,常见区间 4000–10000。统计真实文件数:

find . -type f -name "*.php" | wc -l

取值建议略大于统计结果的素数,避免哈希槽位冲突。

opcache.validate_timestamps:生产强烈建议 0,不再检查文件变化,代价是代码更新后必须手动清缓存;开发环境设 1。

opcache.validate_timestamps=0
opcache.revalidate_freq=0
opcache.fast_shutdown=1

opcache.save_comments=0:只有应用和依赖都不使用注解/注释时才可设 0 省内存,否则保持默认 1。这是有风险的优化,改之前必须先在预发环境验证。

高级选项:

  • opcache.preload(PHP 7.4+):服务启动时预加载框架核心与常用库。被预加载的文件在 FPM 生命周期内不能被修改,更新必须重启 FPM。
  • opcache.huge_code_pages=1:需要操作系统支持 Huge Pages。
  • optimization_level:默认 0x7FFBBFF / 0x7FFFFBFF 一般不动。

监控与清缓存

opcache_get_status() 是直接可用的接口,opcache-gui 提供可视化。关键指标是命中率,应接近 100%;命中率低或未命中数持续增长,通常说明内存不足,或者有文件根本没被缓存进去。

清缓存三条路:systemctl restart php-fpm;部署脚本里调 opcache_reset();用 cachetool 通过 FastCGI 与 FPM 通信查询和清除。

JIT

PHP 8.0 起,JIT 在 OpCache 基础上把热点代码编译成机器码。两种模式:Function JIT 编译整个函数,Tracing JIT 只编译热路径,后者更推荐。

适用场景是 CPU 密集型任务(科学计算、图像处理、算法),提升可达数倍。I/O 密集的 Web 应用提升有限,甚至可能因为编译开销略微下降,必须压测评估。

opcache.jit_buffer_size=128M
opcache.jit=tracing          ; 等价于 1255,最常用
; opcache.jit=1205           ; 触发更保守,混合负载可用

jit_buffer_size 从 128MB/256MB 起。进阶参数有 jit_hot_loopjit_hot_funcjit_max_root_tracesjit_debug

验证 JIT 是否生效:看 phpinfo() 的 opcache/jit 段,或读 opcache_get_status() 返回的 jit 键,里面有 enabled、on、kind、optimization_level、buffer_size、buffer_used。

PHP-FPM 进程管理

FPM 采用 master + worker 多进程模型,单个 worker 崩溃不影响其他进程。pm 有三种模式:

  • static:固定 worker 数量,无创建销毁开销,适合负载稳定、内存充足。
  • dynamic:最常用,按 spare 参数动态增减。
  • ondemand:无请求时降到 0,最省内存但响应慢,适合低负载。

pm.max_children 是最重要的参数,直接决定并发上限。它不是越大越好,过高会耗尽内存触发 swapping。计算方式是:先观察单 worker 的平均内存占用(用 ps 看),再用可分给 FPM 的总内存去除。

8GB 内存,划 6GB 给 FPM,单进程 30MB
(6 * 1024) / 30 ≈ 204

同时要给系统和数据库预留余量。也有「CPU 核心数 2–4 倍」的经验值,但不如按内存算精确。

pm = dynamic
pm.max_children = 204
pm.start_servers = 51            ; CPU 核心数 2-4 倍,或 max_children 的 25%
pm.min_spare_servers = 20        ; 保底空闲进程
pm.max_spare_servers = 60        ; 上限空闲进程

pm.max_requests 取 500–5000,用于防止内存泄漏累积。太小会导致 worker 频繁重启,太大或设 0 则失去作用。

request_terminate_timeout 是单请求最长执行时间,比 max_execution_time 更硬——后者可以被 set_time_limit() 覆盖。普通 Web 请求 30s/60s,长任务应该异步化处理。

request_slowlog_timeout 配合慢日志,超过阈值就记录 PHP 调用栈,用来定位慢函数。阈值设成你「不可接受」的响应时间,2s / 5s / 10s 都有人用。

request_terminate_timeout = 60s
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slow.log
process_control_timeout = 10s
emergency_restart_threshold = 10
emergency_restart_interval = 1m

最后两项的含义是:1 分钟内有 10 个子进程崩溃,就重启整个 FPM。

整体调优策略仍然是基于监控数据、压测结果迭代调整,没有一次到位的配置。

Composer 自动加载优化

官方文档:Composer autoloader optimization

类映射生成把 PSR-4/PSR-0 规则转成 classmap 规则:已知类立即返回路径,不需要做文件系统检查。

Level 1dump-autoload -oinstall|update -o。PHP 5.6+ 的类映射会被 opcache 缓存,初始化几乎瞬时完成,没有真正的代价,生产环境必开。它不跟踪 autoload miss,找不到的类会回退到 PSR-4,仍然会有慢的文件系统检查。

Level 2/A,classmap-authoritative(-a:开启即包含 Level 1。类不在 classmap 里就视为不存在,不再按 PSR-4 查文件系统,始终快速返回。代价是运行时动态生成的类无法自动加载,会直接报 class not found,需要谨慎开启。

Level 2/B,APCu(--apcu-autoloader:用 APCu 作为 classmap 的回退,需要安装 APCu 扩展。无论是否找到类都会缓存,安全,不会像 authoritative 那样导致找不到类。

2/A 与 2/B 不能合并使用。

Composer 2.9.6+ 的行为变化

生产部署务必用 composer install,而不是 dump-autoload。Composer 2.x(尤其 2.9.6+)里,composer dump-autoload -o 基本不再生成 vendor/composer/autoload_classmap.php,只刷新 autoload_static.php 和 PSR-4 映射;真正的 classmap 只由 install/update 扫描写入。所以部署脚本里写 dump-autoload -o 几乎无效,还可能加载一个几 MB 的空 classmap 拖慢冷启动。

正确组合:

composer install --no-dev --optimize-autoloader --classmap-authoritative --no-interaction

--no-dev 防止测试类混进 classmap 导致体积膨胀到 2–5MB,--no-interaction 防止 CI 流程卡在交互提示上。

遇到 Class not found 怎么办

--classmap-authoritative 下报 Class not found 不必慌,这恰恰说明权威模式生效了,只是 classmap 漏了类。常见原因有三类:

  1. composer.json 里 PSR-4 命名空间末尾的反斜杠写错;
  2. 新增类的文件名、命名空间与目录结构不严格一致;
  3. 项目动态调用了 ClassLoader::addPsr4() / addClassMap(),权威模式退化成扫描。

验证 classmap 是否真的生效

# 1. 直接测类是否存在
php -r "require 'vendor/autoload.php'; var_dump(class_exists('App\\SomeClass'));"

# 2. 删掉对应类文件后再跑,若仍为 true,说明 PSR-4 fallback 仍然生效,-a 没起作用

# 3. 检查 autoload_real.php 是否调用 addClassMap
grep -n addClassMap vendor/composer/autoload_real.php

# 4. 看还有没有大量失败的 stat/openat
strace -e trace=stat,openat php your-script.php

另外两点容易踩:镜像源只加速下载,不参与 classmap 生成,类映射是否生效取决于本地 composer.json、命令和环境变量;只有 COMPOSER_DEV_MODE=0--no-dev 才会触发权威模式(Composer 2.2+ 的行为)。

复制全文 生成海报 PHP PHP-FPM OPcache Composer 性能优化 运维

推荐文章

程序员茄子在线接单