PHP Opcache 调优:从命中率 80% 到 99%#
PHP 每请求都要"解析源码 → 编译成字节码 → 执行"。Opcache 把编译好的字节码缓存进共享内存,省掉了解析和编译两步——这是 PHP 性能的第一杠杆。我把线上命中率从 80% 调到 99%,吞吐提升近 3 倍。
一、先看 Opcache 做了什么#
结论:Opcache 生效时,PHP 请求省掉 30-60% 的 CPU(编译开销)。
二、核心配置#
1
2
3
4
5
6
7
8
9
10
11
|
[opcache]
zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1 # CLI 也开(cron 任务受益)
opcache.memory_consumption=256 # 共享内存大小(MB)
opcache.interned_strings_buffer=32 # 字符串驻留缓冲
opcache.max_accelerated_files=50000 # 缓存文件数上限
opcache.validate_timestamps=0 # 生产关掉 mtime 检查
opcache.revalidate_freq=0
opcache.fast_shutdown=1
opcache.opcache.file_cache=/tmp/opcache_file_cache # 文件缓存兜底
|
三、两个关键决策#
决策 1:memory_consumption 给多少#
不够:新文件编译不进缓存,命中率下降,或强制驱逐旧缓存。
1
2
3
|
# 生产查看使用率
php -r "var_dump(opcache_get_status()['memory_usage']);"
# 看 memory_free 是否接近 0
|
够用标准:内存占用率 < 80%(留余量),命中率 > 98%。
决策 2:validate_timestamps 关不关#
- 开(默认):每次请求 stat 文件 mtime → 代码热更新生效,但每个请求多一次磁盘 stat,高并发下是实打实的开销
- 关:快,但改代码后不生效,需要手动清缓存
生产(镜像发布)关掉,开发环境开着。我们代码走 CI 构建镜像发布,文件不变,关掉是纯赚。
四、命中率怎么查#
1
2
3
4
5
6
7
8
|
# 命令行查
php -r "
\$s = opcache_get_status();
echo '命中率: ' . round(\$s['opcache_statistics']['opcache_hit_rate'], 2) . '%';
echo '缓存文件: ' . \$s['opcache_statistics']['num_cached_scripts'] . '/' . \$s['opcache_statistics']['num_cached_keys'] . PHP_EOL;
"
# 或用 opcache-gui 网页版看
|
命中率目标:
- < 90%:有问题(内存不够 / max_files 不够 / 文件未预热)
- 90-97%:还可以
- 98%+:健康
五、K8s 下的冷启动问题(最重要)#
我在《K8s 单 Pod 性能调优》里讲过:Pod 重建 = Opcache 冷缓存,第一个请求要全量编译,30-60s 内吞吐崩。
解法:启动预热#
1
2
3
|
# entrypoint:先预热再启动
php opcache_prewarm.php # 遍历所有 PHP 文件,触发编译
php-fpm -F
|
1
2
3
4
5
6
7
8
|
// opcache_prewarm.php
$files = getAllPhpFiles('/var/www'); // 递归收集
foreach ($files as $f) {
if (opcache_compile_file($f)) { // 预编译进共享内存
$count++;
}
}
echo "prewarmed: $count files\n";
|
注意:预热必须在同一个容器/进程组里做(Opcache 是进程共享内存),跨容器预热无效。
六、和 FPM worker 的配合#
Opcache 内存不够时,多个 worker 争抢会导致 miss 和驱逐——Opcache 内存是全局的,别按 worker 数算,按"全量字节码大小 × 1.5"给。
1
2
3
|
# 估算全量字节码大小
du -sh /var/www/vendor /var/www/app
# 经验:源码 500MB → 字节码约 150-200MB → memory_consumption=256 合适
|
七、效果数据#
| 指标 |
调优前 |
调优后 |
| Opcache 命中率 |
80% |
99%+ |
| 单 Pod QPS |
120 |
320(+167%) |
| P99 延迟 |
380ms |
160ms |
| 编译导致的 CPU 峰值 |
明显 |
消失 |
八、踩坑记录#
- max_accelerated_files 不够:文件数超了,新文件不缓存 → 命中率莫名下降。用
opcache_get_status()['opcache_statistics']['num_cached_keys'] 和文件总数对比
- CLI 没开:cron 任务每次全量编译 → 大批定时任务 CPU 飙高。
enable_cli=1 解决
- 文件缓存目录权限:
opcache.file_cache 目录无权限 → 启动报错,静默降级
- 和 opcache-recommended 冲突:别乱装第三方"推荐配置",以实际命中率为准
Opcache 调优的核心:
- 命中率是北极星指标:一切配置以 98%+ 为目标
- 内存够 + 文件数够:两个"够"是基础
- 生产关 mtime 校验:镜像发布场景纯赚
- K8s 必做预热:冷启动是容器时代的新坑
Opcache 是 PHP 最便宜的优化——不用改代码、不用加机器,配置对了吞吐翻倍。
微信公众号「福清而不淡」
后端架构 · AI 工程 · 云原生的一线实践,扫码关注,不错过更新。
本文已同步发布到公众号,欢迎留言交流。