如何计算 OPcache 命中率?

解读

面试官问“如何算命中率”,表面看是让你写公式,实则考察三层能力:

  1. 是否真正理解 OPcache 在请求生命周期里的位置——避免“重复编译”而非“缓存数据”;
  2. 能否把“命中/未命中”对应到可观测指标,知道国内生产环境最方便拿到的数字是哪些;
  3. 能否把数字变成可落地的监控与告警方案,体现“高并发场景下的运维意识”。

如果只背公式而说不出“去哪取数、怎么采样、告警阈值设多少”,会被认为没有线上经验。

知识点

  1. OPcache 核心指标(php-fpm 运行时可直采)
    • opcache_hit:缓存命中次数(即无需重新编译的次数)
    • opcache_miss:缓存未命中次数(需要重新编译并写入缓存)
    • opcache_hit_rate:部分扩展直接给出的百分比,等同于 hit/(hit+miss)
  2. 官方扩展函数:opcache_get_status(false) 返回数组,关注
    ['opcache_statistics']['hits']
    ['opcache_statistics']['misses']
  3. 国内主流采样方式
    • _exporter 方案:GitHub 开源的 php-fpm_exporter 或阿里云 SLS 插件,每 10s 拉一次 status;
    • 自研脚本:crontab 每分钟 curl 本地 /opcache-status.php(仅允许 127.0.0.1),把 hits、misses 打入 Prometheus;
    • 容器场景:把上述脚本打镜像,通过 sidecar 暴露 metrics 接口,供夜莺或阿里云 ARMS 抓取。
  4. 阈值经验(电商、CMS、SaaS 通用)
    • 日峰 95 分位命中率 < 97% 即黄色告警;
    • 持续 5min < 92% 红色告警,需检查是否代码热部署过快、内存不足或 validate_timestamps 策略过激进。

答案

“OPcache 命中率 = 命中次数 ÷(命中次数 + 未命中次数)× 100%。
线上取数最直接的方式是调用 opcache_get_status()['opcache_statistics'],拿到 hits 与 misses 两个整型累加值,实时计算即可。
举例:
hits = 19876300,misses = 401700,
命中率 = 19876300 / (19876300 + 401700) ≈ 98.02%。
在 Prometheus 里可写成:
rate(phpfpm_opcache_hits_total[5m]) / (rate(phpfpm_opcache_hits_total[5m]) + rate(phpfpm_opcache_misses_total[5m])) * 100
告警规则当该值 < 97% 持续 5min 就发钉钉。”

拓展思考

  1. 命中率掉底常见根因
    • 代码发布脚本每次 rsync 后批量 touch 文件,导致全部缓存失效;
    • opcache.memory_consumption 设置过小,新脚本不断挤掉旧脚本;
    • 文件数量突增(如生成大量代理类),max_accelerated_files 默认值 4000 不够。
  2. 优化手段
    • 发布采用“软链切换目录”或“蓝绿目录”,避免时间戳抖动;
    • 调大 memory_concelerated_files,并开启 interned_strings_buffer=32M 以上;
    • 关闭 validate_timestamps(生产只读挂载),或把 revalidate_freq 调到 300s 以上;
    • 对容器冷启动场景,使用 opcache.preload 把核心框架提前编译,减少首批 miss。
  3. 面试加分项
    • 提到“命中率 100% 不一定最优”,需结合内存占用、OOM 风险权衡;
    • 能把 OPcache 与 Realpath cache、APCu 做分层解释,体现对 Zend 引擎缓存体系的完整认知;
    • 给出“灰度 + 回滚”方案:命中率告警触发后 30s 内自动回滚上一次发布,保障大促稳定性。