当前位置:首页 > 文章列表 > 文章 > php教程 > OPcache 更新代码后仍命中旧脚本,该检查哪些配置

OPcache 更新代码后仍命中旧脚本,该检查哪些配置

来源:17golang原创 2026-10-08 19:42:09 0浏览 收藏

PHP 代码已经更新,页面却仍像在执行旧脚本,优先检查的不是浏览器缓存,而是实际承载请求的 PHP 进程、时间戳校验策略、发布绝对路径,以及 file cache 或 preload。最常见的误区是:在命令行执行了 opcache_reset(),但线上请求由 PHP-FPM 提供;CLI 与 Web SAPI 的配置和 OPcache 实例并不是同一个。

官方配置文档:https://www.php.net/manual/en/opcache.configuration.php

官方函数文档:https://www.php.net/manual/en/ref.opcache.php

按优先级从高到低依次排查即可
  1. 从真实 Web 请求确认 SAPI、进程池和实际加载的 php.ini。
  2. 检查 opcache.validate_timestamps 与 opcache.revalidate_freq。
  3. 核对请求命中的容器、节点、发布目录和脚本绝对路径。
  4. 少量文件用 opcache_invalidate(),大范围变更用 Web SAPI 内的 opcache_reset() 或平滑重启。
  5. 继续检查 opcache.file_cache、opcache.preload、opcache.use_cwd 和 opcache.revalidate_path。

先确认:你查的是哪个 PHP 进程

同一台机器可以同时存在 CLI、多个 PHP-FPM Pool、不同 PHP 版本或多个容器。它们可能加载不同的配置文件,也可能拥有不同的 OPcache 共享内存。PHP 官方文档特别说明,命令行进程的 opcode cache 与 Web 服务器或 PHP-FPM 的缓存是分开的,因此在 CLI 中调用 opcache_reset(),不会替线上 FPM 清掉缓存。

不要只看 php -i。应该临时建立一个只允许部署系统访问的内部诊断入口,在真实域名、真实负载均衡路径下读取有效配置。下面示例只返回定位所需字段;实际使用时仍应放在内网并接入现有部署鉴权,排查结束后删除。

 PHP_SAPI,
    'pid' => getmypid(),
    'loaded_ini' => php_ini_loaded_file(),
    'opcache_enabled' => $status['opcache_enabled'] ?? false,
    'restart_pending' => $status['restart_pending'] ?? false,
    'validate_timestamps' => $directives['opcache.validate_timestamps'] ?? null,
    'revalidate_freq' => $directives['opcache.revalidate_freq'] ?? null,
    'file_update_protection' => $directives['opcache.file_update_protection'] ?? null,
    'file_cache' => $directives['opcache.file_cache'] ?? null,
    'preload' => $directives['opcache.preload'] ?? null,
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

如果请求轮询几次后出现不同 PID、不同配置值或不同应用版本,问题已经不只是单个 OPcache:负载均衡后可能存在多个未同步的 FPM Pool、Pod 或主机。此时要让每个实例都执行同一部署动作,或先从流量中摘除旧实例。

Web 请求、PHP-FPM 进程池、CLI SAPI、配置文件与 OPcache 缓存实例的静态边界图
图1:Web 请求可能被分流到不同 PHP-FPM Pool,而 CLI 拥有独立配置与缓存边界;诊断必须落在实际承载流量的进程域。

最关键的配置组合:validate_timestamps 与 revalidate_freq

opcache.validate_timestamps 决定 OPcache 是否检查脚本文件的修改时间。启用时,检查间隔由 opcache.revalidate_freq 控制;设为 0 表示每次请求都检查时间戳。关闭时间戳校验后,revalidate_freq 不再起作用,代码变化必须通过单文件失效、缓存重置或 Web 进程重启才能被加载。

配置实际行为发布要求
validate_timestamps=1
revalidate_freq=0
每次请求检查脚本时间戳适合开发或更新频繁环境,运行开销相对更高
validate_timestamps=1
revalidate_freq=2
最多存在约一个检查间隔的旧代码窗口发布后短暂旧结果可能是预期行为
validate_timestamps=0文件改变不会被自动发现部署流程必须显式失效、重置或重启

因此,看到“偶尔两秒后恢复”时先看 revalidate_freq;看到“永远不恢复,重启才好”时优先看 validate_timestamps=0、preload 或错误的刷新进程。

更新了文件,为什么路径仍可能不对

现代部署经常把新版本写入新目录,再把 current 软链接切到新 release。对 OPcache 来说,绝对路径、解析后的真实路径和进程已经缓存的脚本键都很重要。只对旧路径调用失效,新的请求却命中新路径;或者多个实例仍挂载旧 release,都可能表现为“明明更新了还在跑旧代码”。

先在业务响应中临时输出一个无敏感信息的构建号,再把以下内容一起核对:

  • 每个实例的构建号是否一致;
  • 入口脚本的 realpath() 是否指向当前 release;
  • PHP-FPM 工作目录、DocumentRoot 和容器挂载是否一致;
  • 代码是否以原子替换方式落盘,还是原地覆盖正在读取的文件;
  • opcache.file_update_protection 是否让刚写入的文件暂时不进入缓存。

opcache.file_update_protection 的默认值为 2 秒,用于避免缓存尚未写完的新文件。如果发布采用完整目录加原子切换,可以评估把它设为 0;如果仍是原地写文件,则不应为了“立即生效”贸然关闭保护。

单文件失效、全量重置和进程重启怎么选

变更文件少、绝对路径明确时,优先用 opcache_invalidate($filename, true)。第二个参数为 true 时,不依赖 mtime 是否变新,适合原子替换或保留时间戳的部署流程。

如果一次发布修改了大量 PHP 文件,可从实际承载 Web 请求的 SAPI 调用 opcache_reset()。它会重置当前内存 opcode cache,脚本在下一次请求时重新载入,但不会清理 opcache.file_cache 指向的二级文件缓存。

以下情况更适合平滑重载或重启 PHP 进程:启用了 preload;无法保证所有 FPM Pool/Pod 都收到失效请求;发布涉及大量文件并要求版本强一致;或者需要同时切换 PHP 扩展和配置。刷新接口本身具有高影响力,只能作为部署系统内部能力,不能留成公开 URL。

OPcache 时间戳策略、发布绝对路径、单文件失效、全量重置、二级文件缓存与预加载的静态关系图
图2:时间戳策略决定自动发现窗口,发布路径决定失效目标;内存重置、文件缓存与预加载属于不同层级。

仍未解决时,再查这四项

1. opcache.file_cache

配置了 opcache.file_cache 后,磁盘目录会成为二级缓存。官方说明 opcache_reset() 只重置内存缓存,而 opcache_get_status() 也不提供 file cache 状态。因此,内存重置后仍旧时,要核对 file cache 路径、目录权限与部署清理策略;单文件 opcache_invalidate() 会同时让对应的文件缓存条目失效。

2. opcache.preload

预加载脚本在服务器启动时加载。PHP 官方预加载文档明确指出,已预加载的代码不能靠普通缓存重置更新,必须重启 PHP 进程。如果旧类来自 preload 文件或其依赖链,继续反复调用 opcache_reset() 没有意义。

3. opcache.use_cwd 与 opcache.revalidate_path

opcache.use_cwd=1 会把当前工作目录加入缓存键,避免不同目录下同名脚本发生碰撞。关闭它可以节省少量键空间,但多个应用存在同名脚本时风险更高。opcache.revalidate_path=0 时,通过 include_path 找到并缓存的同名文件可能被复用,之后同名的其他文件不一定被重新搜索。遇到插件目录、共享库或多站点同名文件时,应同时检查这两项。

4. 状态数据是否证明命中了旧脚本

opcache_get_status(true) 可以返回内存缓存中的脚本信息,包括命中次数、内存消耗和时间戳等。PHP 8.3 及以后,脚本项还可能包含下次校验时间 revalidate。这类明细可能暴露服务器路径,只能在受保护的诊断环境使用,不应直接返回给公网用户。

一份可执行的发布排查清单

  1. 请求真实域名,确认响应的构建号与目标版本一致。
  2. 从该请求所在 FPM 读取 PHP_SAPI、PID、php_ini_loaded_file() 与 OPcache 有效指令。
  3. 连续请求多次,检查是否命中不同实例、不同 Pool 或不同版本。
  4. 若启用时间戳校验,等待一个 revalidate_freq 后复测。
  5. 若关闭时间戳校验,对真实绝对路径执行 opcache_invalidate(..., true),或在 Web SAPI 内重置。
  6. 若使用 preload,直接走 PHP 进程平滑重载或重启。
  7. 若启用 file cache,检查二级缓存目录与部署清理策略。
  8. 核对 realpath()、软链接目标、容器挂载和每个实例的 release 目录。

常见问题

把 revalidate_freq 设为 0 就一定立即生效吗?

不一定。它只有在 opcache.validate_timestamps=1 时才生效,也解决不了错误进程池、旧 Pod、file cache、preload 或发布路径不一致。

为什么 CLI 里 reset 返回 true,网页仍是旧代码?

因为 CLI 与 PHP-FPM 的 opcode cache 分离。返回 true 只说明 CLI 所属缓存执行了重置,不代表承载网页请求的 FPM 缓存被处理。

每次发布都重启 PHP-FPM 可以吗?

可以作为强一致的部署策略,尤其在关闭时间戳校验或使用 preload 时,但应使用进程管理器提供的平滑重载能力,并确认连接排空、健康检查和多实例滚动顺序。只改少量普通脚本时,精确失效通常影响更小。

归纳起来,旧脚本问题要按“请求落在哪个进程—该进程加载什么配置—脚本使用哪个绝对路径—缓存位于哪一层”来查。先拿到真实 FPM 的有效配置和构建号,再选择失效、重置或重启,通常比盲目反复清缓存更快。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
第三方模块停止维护时,替换、分叉与隔离该怎么选第三方模块停止维护时,替换、分叉与隔离该怎么选
上一篇
第三方模块停止维护时,替换、分叉与隔离该怎么选
封装 C 库句柄并明确创建、释放与线程约束
下一篇
封装 C 库句柄并明确创建、释放与线程约束
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    379次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    450次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    460次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    402次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    231次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码