登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  php教程

PHP OPcache 缓存命中 怎样处理

来源:17golang原创

时间:2026-10-22 08:02:57 104浏览 收藏

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 当作共享缓存身份。诊断异常时不应把告警详情、安装路径或完整配置发送给普通访客。

OPcache状态函数、统计字段与最小摘要的组成关系
静态结构图:状态函数提供统计和运行标记,最小摘要不包含逐脚本列表。

累计命中率为什么不等于最近一段时间?

长期累计值会把缓存生命周期内的不同负载混在一起。想知道一次调整之后的变化,应对同一缓存实例保留前后两个样本,用 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 OPcache 状态函数;PHP OPcache 配置说明。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>