PHP 8 性能调优:OpCache 与 JIT 详解
你的 PHP 8 应用在负载下感觉迟缓。你已经优化了查询,添加了缓存,但响应时间仍然滞后。罪魁祸首可能是 PHP 自身的执行开销。两个强大的功能——OpCache 和 JIT——可以显著加速你的代码,但前提是配置正确。本指南解释了它们的工作原理以及如何针对真实工作负载进行调优。
什么是 OpCache 以及为什么它很重要
PHP 是一种解释型语言:每个请求通常在执行前解析并编译源文件为字节码。OpCache 将编译后的字节码存储在共享内存中,因此后续请求会跳过解析和编译。结果呢?更低的 CPU 使用率和更快的响应时间。
在许多生产环境中,OpCache 默认启用,但默认设置较为保守。对其进行调优可以为 CPU 密集型应用带来 2-3 倍的吞吐量提升。
关键的 OpCache 配置指令
- opcache.enable:设置为 1 以启用。(默认值:生产环境为 1,CLI 为 0,除非覆盖。)
- opcache.memory_consumption:共享内存大小,单位为 MB。从 128 MB 开始;如果看到较高的
opcache.memory_usage,则增加。 - opcache.max_accelerated_files:缓存的最大文件数。设置为大于 PHP 文件总数的质数(例如 10007)。
- opcache.validate_timestamps:当为 1 时,PHP 检查文件时间戳以检测更改。在生产环境中设置为 0 以避免 stat 调用;部署后必须手动清除缓存。
- opcache.revalidate_freq:如果 validate_timestamps=1,检查时间戳的频率(秒)。开发环境设置为 0,生产环境设置为 60+。
- opcache.interned_strings_buffer:驻留字符串的内存(MB)。对于大型应用,增加到 16 或 32。
- opcache.fast_shutdown:在 PHP 7.2+ 中已弃用,在 PHP 8.0 中移除。忽略。
- opcache.preload:预加载脚本的路径(PHP 7.4+)。预加载在启动时编译和缓存文件,消除关键类的每请求开销。
生产环境推荐的 OpCache 设置
; php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
opcache.interned_strings_buffer=32
opcache.preload=/path/to/preload.php
opcache.preload_user=www-data
更改设置后,重启 PHP-FPM。使用 opcache_get_status() 监控以检查命中率和内存使用情况。
理解 PHP 8 中的 JIT
JIT(即时编译)将 OpCache 更进一步:它在运行时将字节码编译为原生机器码。这可以显著加速 CPU 密集型任务,如数学计算,但对于典型的由 I/O(数据库、文件系统、网络)主导的 Web 请求几乎没有帮助。
JIT 是 OpCache 的一部分,因此必须首先启用 OpCache。在 PHP 8 中,JIT 通过 opcache.jit 和 opcache.jit_buffer_size 配置。
JIT 配置指令
- opcache.jit:控制 JIT 模式。使用
tracing(默认)或function。该值是一个 4 位 CRTO 代码:CRTO,其中 C=CPU 特定优化,R=寄存器分配,T=触发,O=优化级别。对于大多数应用,opcache.jit=tracing即可。 - opcache.jit_buffer_size:JIT 代码的内存,单位为字节。从 64M 或 128M 开始。如果设置为 0,则禁用 JIT。
- opcache.jit_hot_loop、opcache.jit_hot_func、opcache.jit_hot_return、opcache.jit_hot_side_exit:热代码检测的阈值。默认值通常可以。
推荐的 JIT 设置
opcache.jit=tracing
opcache.jit_buffer_size=128M
注意:JIT 仅在启用 OpCache 时有效。在 CLI 中,可能需要设置 opcache.enable_cli=1 来测试 JIT。
OpCache vs JIT:何时使用哪个
| 特性 | OpCache | JIT |
|---|---|---|
| 主要优势 | 缓存编译后的字节码 | 将字节码编译为原生代码 |
| 最适合 | 所有 PHP 应用 | CPU 密集型、长时间运行的脚本 |
| 对 I/O 密集型应用的影响 | 高(减少解析/编译) | 低到无 |
| 内存开销 | 中等 | 额外的缓冲区 |
| 复杂性 | 低 | 中 |
逐步调优指南
- 启用 OpCache,使用上述推荐设置。
- 监控 OpCache,使用
opcache_get_status()或 OpCache GUI 等工具。目标是命中率超过 99%。 - 调整内存,如果
opcache.memory_usage接近限制。 - 考虑预加载,对于大型框架(Laravel、Symfony)。创建一个预加载脚本,包含经常使用的类。
- 启用 JIT,仅当你的应用是 CPU 密集型时。测试有和没有 JIT 的影响。
- 基准测试,使用
ab、wrk或siege等工具。比较每秒请求数和延迟。 - 逐步部署更改,并监控错误日志以发现问题。
常见陷阱和最佳实践
- 不要在开发环境中禁用 validate_timestamps:你会浪费时间想知道为什么代码更改没有生效。
- 部署时清除 OpCache:如果 validate_timestamps=0,必须重置 OpCache(例如,通过重启 PHP-FPM 或调用
opcache_reset())。 - JIT 不是银弹:对于典型的 CRUD 应用,仅 OpCache 就能提供大部分收益。
- 注意内存:JIT 缓冲区和 OpCache 内存会增加 PHP 的内存占用。确保服务器有足够的 RAM。
- 谨慎使用预加载:预加载的文件始终在内存中;避免预加载所有内容。
常见问题
JIT 能提高所有 PHP 应用的性能吗?
不能。JIT 有利于 CPU 密集型任务,如复杂计算或数据处理。对于 I/O 密集型 Web 应用(数据库查询、API 调用),收益微乎其微。仅 OpCache 通常就能提供最大的加速。
如何知道 OpCache 是否在工作?
检查 phpinfo() 中的 OpCache 状态,或在脚本中调用 opcache_get_status()。查找 opcache_enabled => true 以及相对于 misses 较高的 hits 计数。
我可以在 PHP 7 中使用 JIT 吗?
不能。JIT 是在 PHP 8.0 中引入的。PHP 7 仅支持 OpCache。如果你使用的是 PHP 7,请专注于 OpCache 调优,并考虑升级到 PHP 8 以获得 JIT 和其他性能改进。
准备好分析你的服务器性能了吗?查看我们的 Nginx 日志分析器,从访问日志中获取见解并识别瓶颈。