PHP OPcache 缓存命中 怎样处理
PHP OPcache 缓存命中 怎样处理
OPcache 命中率偏低时,先确认统计来自业务使用的 PHP 运行环境和缓存实例,再区分累计指标与观察区间。不要把 CLI 的一次输出当作 PHP-FPM 的状态,也不要通过反复清缓存来“修复”命中率。缓存刚初始化、发布后重新编译和持续容量压力,需要不同处理。
官方文档入口:https://www.php.net/manual/en/function.opcache-get-status.php。下文提供只读诊断片段与计算方法,不执行缓存重置,也不声称已经在生产环境测得任何收益。
命中的是字节码,还是页面内容?
OPcache 解决的是 PHP 编译后字节码的复用,不是把整个业务响应永久保存下来。业务仍可能访问数据库、调用远程接口和执行业务逻辑。因此,即使命中统计很好,接口也可能因为其他依赖而变慢;一次 HTTP 请求也不能简单对应一个 OPcache 命中。
排查前建议写清观察对象,例如某台实例的 Web PHP 环境,并记录部署批次、采样时间和运行环境标识。这里的“缓存实例”不能武断地等同于每个请求进程,也不能认定不同 FPM 池一定各有独立统计,实际共享边界由部署方式决定。
怎样读取状态,又不输出全部脚本信息?
官方函数返回内存缓存实例的信息,不返回文件缓存信息。将 include_scripts 设为 false 可避免包含逐脚本状态。下面代码适合放在已有的内部管理诊断入口中,前提是应用已经完成访问控制;不要把它保存成任何人都能访问的公开 PHP 文件。
false, 'reason' => 'function_unavailable'];
}
// 不索取逐脚本列表;访问限制产生的告警由内部日志处理
$status = opcache_get_status(false);
if (! is_array($status) || empty($status['opcache_enabled'])) {
return ['available' => false, 'reason' => 'status_unavailable'];
}
$stats = $status['opcache_statistics'] ?? [];
$memory = $status['memory_usage'] ?? [];
// 未提供的字段保留null,不把未知值伪装成零
return [
'available' => true,
'sapi' => PHP_SAPI,
'sampled_at' => time(),
'hits' => $stats['hits'] ?? null,
'misses' => $stats['misses'] ?? null,
'start_time' => $stats['start_time'] ?? null,
'last_restart_time' => $stats['last_restart_time'] ?? null,
'cache_full' => $status['cache_full'] ?? null,
'restart_pending' => $status['restart_pending'] ?? null,
'free_memory' => $memory['free_memory'] ?? null,
'oom_restarts' => $stats['oom_restarts'] ?? null,
'hash_restarts' => $stats['hash_restarts'] ?? null,
];
}
这段函数只返回摘要,不负责 HTTP 输出、登录鉴权或持久化;内部监控调用后应按自身规范保存。状态不可用与命中率为零不是同一个结论。若配置 opcache.restrict_api,入口路径不符合限制可能触发告警,此时应由管理员调整合法诊断入口,而不是为了采样随意取消限制。
同时保留采集器提供的实例标识和缓存所属运行环境,不要直接用请求进程 PID 当作共享缓存身份。诊断异常时不应把告警详情、安装路径或完整配置发送给普通访客。

累计命中率为什么不等于最近一段时间?
长期累计值会把缓存生命周期内的不同负载混在一起。想知道一次调整之后的变化,应对同一缓存实例保留前后两个样本,用 hits 差值除以 hits 与 misses 差值之和。分母为零时,区间没有足够的计数活动,应显示“不适用”,不要强行写成 100%。
例如,假设前后 hits 分别为 800、980,misses 分别为 200、220,则区间比例是 180 除以 200,即 90%。这些数字只是算术例子,不是运行结果,也不是任何业务应达到的阈值。
// 调用方已确认a、b来自同一缓存实例,且b采样时间晚于a
// 先核对所需字段,避免对不可用状态或null进行算术运算
$required = ['hits', 'misses', 'start_time', 'last_restart_time'];
$comparable = ! empty($a['available']) && ! empty($b['available']);
foreach ($required as $key) {
$comparable = $comparable
&& isset($a[$key], $b[$key])
&& is_numeric($a[$key]) && is_numeric($b[$key]);
}
$ratio = null;
if ($comparable
&& $a['start_time'] === $b['start_time']
&& $a['last_restart_time'] === $b['last_restart_time']) {
$dh = $b['hits'] - $a['hits'];
$dm = $b['misses'] - $a['misses'];
// 计数回退或无活动时不生成比例;标记相同也不是身份的充分证明
if ($dh >= 0 && $dm >= 0 && ($dh + $dm) > 0) {
$ratio = $dh / ($dh + $dm);
}
}
a、b 是调用方保存的两份摘要,不是需要在一个 Web 请求里长时间等待采样的理由。示例计算的 ratio 为 0 到 1 的比例,展示百分数时再乘 100。若采样跨重启、实例替换或统计周期变化,就开启新观察段,不把它和旧段硬接在一起。

未命中增加时,先看哪些上下文?
先区分发布后的冷启动与平稳运行中的变化。应用刚开始执行更多脚本,可能出现新的编译工作;采样窗口过短,也容易被少量活动放大。观察时应记录同一实例的发布状态与负载,不能把新旧版本的两个不同实例拼成前后对比。
若 cache_full、剩余内存和重启相关计数同时提示压力,再结合真实配置评估容量与缓存键数量。不要只凭命中率偏低就把内存加倍,更不要把某一个标记单独解释为唯一根因。这里保留 oom_restarts、hash_restarts,是为区分不同重启线索,不是说它们是进程崩溃次数。
还要核对脚本是否在预期缓存范围内,应用是否频繁改变代码文件及路径。诊断应有稳定观察段和明确变更记录,一次只处理一个已确认的原因。本文不提供脱离应用规模的通用容量数值。
能否关闭时间戳检查来提高数字?
不要只为让命中指标好看而关闭 opcache.validate_timestamps。官方配置说明指出,关闭后文件系统变更需要通过明确的缓存刷新或服务重启安排生效;这首先是发布一致性问题,而不是一个随手切换的优化开关。
采用这类部署方式,需要有覆盖各实例的更新及回退方案。本文不执行 opcache_reset,也不建议在业务请求里周期性重置缓存。反复清空会改变观察条件,甚至让刚建立的缓存再次变冷。
几个容易混淆的判断
| 现象 | 正确区分 |
|---|---|
| CLI状态不可用 | 先核对CLI配置,不推断Web状态 |
| 区间分母为零 | 无可计算比例,不代表完美命中 |
| 计数回退或重启标记变化 | 重新建立观察段 |
| 命中率高但请求慢 | 继续分析业务执行和外部依赖 |
为什么 CLI 与 Web 结果不同?它们可能使用不同配置,CLI 还有独立的启用选项,不能据此互相代替。多实例能否直接平均命中率?先确认窗口和数据有效,再汇总计数差值;不同分母的比例简单平均会失真。false 是否表示 misses 为零?不是,它表示没有可用状态结果,应单独处理。
处理顺序是明确运行范围、获取最小状态、建立可比较窗口,再结合容量和发布背景解释变化。缓存命中不是业务正确性或整站性能的保证,不需要为了追求一个百分比牺牲诊断边界。
PHP PHP-FPM worker 并发 怎样处理
- 上一篇
- PHP PHP-FPM worker 并发 怎样处理
- 下一篇
- PHP 继承方法类型兼容 怎样处理
-
- 文章 · php教程 | 14小时前 | PHP · laravel · 重试 · php queue:work Laravel Queue 队列重试 queue:retry failed_jobs
- PHP Laravel Queue 重试 怎样处理
- 326浏览 收藏
-
- 文章 · php教程 | 14小时前 |
- PHP json_encode 返回 false 怎样处理
- 124浏览 收藏
-
- 文章 · php教程 | 14小时前 |
- PHP stream 分块大文件 怎样处理
- 412浏览 收藏
-
- 文章 · php教程 | 14小时前 |
- PHP DateTime 跨时区日期差 怎样处理
- 226浏览 收藏
-
- 文章 · php教程 | 14小时前 |
- PHP 继承方法类型兼容 怎样处理
- 353浏览 收藏
-
- 文章 · php教程 | 14小时前 |
- PHP PHP-FPM worker 并发 怎样处理
- 172浏览 收藏
-
- 文章 · php教程 | 14小时前 | 消息队列 · PHP · 进程管理 · Symfony Messenger messenger:consume --limit --time-limit --memory-limit Worker生命周期
- Symfony Messenger Worker 怎样限制单次处理数量
- 341浏览 收藏
-
- 文章 · php教程 | 14小时前 |
- PHP Eloquent 关系字段 怎样处理
- 394浏览 收藏
-
- 文章 · php教程 | 14小时前 |
- Composer why 怎样查出依赖是被谁引入
- 213浏览 收藏
-
- 文章 · php教程 | 14小时前 |
- PHP 8.5 管道操作符返回值 怎样处理
- 441浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 592次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 628次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 661次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 631次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 445次使用
-
- PHP JSON_THROW_ON_ERROR 抛错后怎么保留原始字段位置
- 2026-09-09 501浏览
-
- PHP 8.5 array_last() 怎么处理空数组:从 null 结果到兼容旧版本的 Polyfill
- 2026-08-16 501浏览
-
- 宝塔配置Ruby环境:RVM+Nginx反代教程
- 2026-05-29 501浏览
-
- unset函数作用范围详解
- 2026-05-29 501浏览
-
- VS Code配置Xdebug教程:PHP调试技巧全解析
- 2026-05-13 501浏览

