OPcache是PHP运行必需的底层加速器,缓存opcode以跳过编译开销,但仅加速脚本执行,不解决数据获取瓶颈;生产必须关闭validate_timestamps并合理配置内存与文件数,且需配合APCu和Redis分层缓存。

OPcache 为什么必须开,且不能只靠它
OPcache 是 PHP 运行时的底层加速器,不是“可选优化”,而是现代 PHP 应用的启动门槛。它缓存的是 Zend 引擎编译后的 opcode,跳过每次请求的词法分析、语法解析和编译阶段——这部分开销在高并发下非常可观。但它的作用范围仅限于 PHP 脚本本身,对数据库查询、API 调用、模板渲染等上层逻辑完全无感。
常见错误现象包括:启用了 opcache.enable=1 却发现页面响应没明显提升;或误以为开了 OPcache 就不用管 Redis 缓存了。本质是混淆了“执行脚本快”和“获取数据快”两个维度。
-
opcache.memory_consumption设太小(如默认 64MB)会导致频繁淘汰,尤其项目文件多时,opcache.max_accelerated_files不够会直接拒绝缓存新脚本 - 生产环境必须设
opcache.validate_timestamps=0,否则每秒都去磁盘比对文件修改时间,反而拖慢响应 - CI/CD 部署后要主动触发
opcache_reset()或重启 PHP-FPM,否则旧opcode仍被使用,导致代码更新不生效
Local Cache(APCu)该用在哪些地方
APCu 是进程内共享内存缓存,适合存小而热、生命周期短、无需跨服务器同步的数据,比如配置片段、路由映射表、权限白名单。它比 Redis 快一个数量级(微秒 vs 毫秒),但容量受限、不持久、多 FPM worker 间需靠共享内存同步,且重启即丢。
典型误用是拿 APCu 存用户 session 或订单状态——这类数据一旦进程重启就丢失,又无法被其他机器感知,极易引发状态不一致。
立即学习“PHP免费学习笔记(深入)”;
- 用
apcu_store('route_map', $routes, 300)缓存路由定义,5 分钟自动过期,避免每次请求都 require 路由文件 - 避免缓存大对象(如完整用户模型),APCu 内存碎片化严重,
apcu_sma_info()可查剩余可用块大小 - 不要和 OPcache 混用同名 key,APCu 的
apcu_fetch()和 OPcache 的opcache_get_status()完全无关
Redis 作为分布式层,关键在失效策略设计
Redis 是真正意义上的“数据缓存”,承担跨进程、跨机器的数据共享职责。但它不是万能胶水——如果所有数据都往 Redis 塞,反而可能因网络延迟或连接池打满拖垮整体性能。重点在于明确什么该进 Redis、怎么设过期、怎么防穿透。
最常踩的坑是缓存雪崩:大量 key 同一时刻过期,瞬间压垮数据库。比如批量设置 EX 3600,凌晨一点整集体失效。
- 对非严格实时数据,用随机偏移:写入时
EX (3600 + rand(0, 600)),把过期时间打散 - 查不到数据时,写入空值并设较短 TTL(如 60 秒),防止缓存穿透
- 高频读+低频写场景,用互斥锁(
SET key value EX 30 NX)控制回源并发,避免 DB 被打爆 - 别把 session 全扔 Redis——PHP 自带
session.save_handler = redis就够用,无需自己封装
多级缓存串联时的顺序与 fallback 逻辑
真实请求链路不是“先 APCu 再 Redis”,而是按访问频率和时效性分层拦截:OPcache 拦截脚本加载 → APCu 拦截配置/元数据 → Redis 拦截业务数据 → 最终 DB 回源。每一层都要有明确的 fallback 和降级路径。
容易被忽略的是异常情况下的缓存污染:比如 Redis 连接超时,代码没做判空直接把 false 存进 APCu,下次请求就永远返回空。
- 检查 Redis 返回值是否为
false或抛出RedisException,失败时跳过写入,不降级到本地缓存 - APCu 缓存应设较短 TTL(如 60 秒),即使 Redis 不可用,最多影响一分钟内的热点数据
- OPcache 的
opcache.revalidate_freq在开发环境设为 2,生产关掉;APCu 和 Redis 的 TTL 则必须按业务语义设定,不能套用同一套数值



















