Skip to content

PHP OPcache

什么是 PHP OPcache?

OPcache(操作码缓存)是 PHP 内置的一个字节码缓存引擎。它的主要目的是通过将预编译的脚本字节码存储在共享内存中,来显著提高 PHP 的性能。这消除了 PHP 在每次请求时都需要加载、解析和编译脚本的需要。

OPcache 解决的问题

在没有 OPcache 的情况下,每次请求 PHP 脚本时都会发生以下步骤:

  1. 读取: Zend 引擎从文件中读取 PHP 源代码。
  2. 解析和编译: 它解析代码,检查语法错误,并将其编译成一种称为操作码 的中间格式。
  3. 执行: 然后引擎执行该操作码。

步骤 1 和 2 对每个请求都会重复执行,这在流量高的站点上是一个巨大的开销。

使用 OPcache 后:

  • 在脚本首次被请求后,编译好的操作码会被存储在共享内存(即 OPcache)中。
  • 对于所有后续的请求,引擎可以跳过读取、解析和编译步骤,直接执行内存中的操作码。

这会带来:

  • 更快的响应时间
  • 更低的 CPU 使用率
  • 更高的吞吐量(每秒可服务更多请求)

关键配置指令

OPcache 在 php.ini 文件中进行配置。以下是一些最重要的设置:

1. 启用 OPcache

ini
; 启用 Zend OPcache
opcache.enable=1

; 为 CLI 版本的 PHP 启用 OPcache(对 Composer 等一些工具有用)
; opcache.enable_cli=0

2. 内存设置

ini
; 用于存储预编译 PHP 文件的内存量(以 MB 为单位)。
; 一个好的起点是 128 或 256。根据您的应用程序大小进行调整。
opcache.memory_consumption=256

; 可以存储在缓存中的 PHP 文件的最大数量。
; 请确保此数量大于您项目中的文件数。
opcache.max_accelerated_files=10000

3. 验证和重新验证

这些设置控制 OPcache 如何检查您的源代码是否已更改。

ini
; OPcache 检查脚本更新的频率(以秒为单位)。
; 0 表示每次请求都检查(对性能不利)。
; 在生产环境中,设置较高的值对性能更有利。
opcache.revalidate_freq=60

; 如果启用,OPcache 将检查文件时间戳以了解更改。
; 禁用它(设为 0)可以稍微提升性能,但需要手动重置才能看到更改。
opcache.validate_timestamps=1

; 如果启用,OPcache 将使用更积极的文件存在性检查,这有助于解决一些边缘情况。
opcache.revalidate_path=0

4. 性能优化

ini
; 通过优化内部字符串缓冲区来节省少量内存并可能提高性能。
opcache.interned_strings_buffer=16

; 通过单个内存释放调用来实现更快的关闭。
opcache.fast_shutdown=1

5. 预加载(PHP 7.4+)

这是一个强大的功能,可以在服务器启动时将您最重要的代码加载到内存中。

ini
; 启用预加载
opcache.preload=/path/to/your/preload.php

; 用于预加载的用户 ID。通常需要与您的 Web 服务器用户(例如 www-data)相同。
; opcache.preload_user=www-data

您的 preload.php 文件应包含 opcache_compile_file() 语句来预加载特定的类和脚本。

php
// preload.php
<?php
$files = /* 指向您关键 PHP 文件路径的数组 */;
foreach ($files as $file) {
    opcache_compile_file($file);
}

检查 OPcache 状态

您可以通过几种方式检查 OPcache 是否在工作并查看其统计信息:

1. 使用 phpinfo()

创建一个简单的 PHP 文件并在浏览器中查看。

php
<?php phpinfo();

寻找标题为 "Zend OPcache" 的部分。如果存在,则表示 OPcache 已启用。

2. 使用内置的 OPcache 状态页面(如果可用)

许多发行版在编译 PHP 时启用了 opcache.file_cache_only=0,这允许您使用 opcache_get_status() 函数。您可以创建一个简单的脚本来显示它:

php
<?php
$status = opcache_get_status(false);
echo '<pre>';
print_r($status);
echo '</pre>';

这将显示详细信息,如内存使用情况、命中率、缓存的脚本等。

3. 使用图形界面

有一些优秀的开源工具可以为监控 OPcache 提供漂亮的 GUI:

常见问题及解决方案

1. "我看不到我的代码更改!"

这是因为 OPcache 正在提供旧的缓存版本。

  • 解决方案 1(开发环境): 在您的开发环境中,设置 opcache.validate_timestamps=1opcache.revalidate_freq=0。这会使其在每次请求时都检查更改。
  • 解决方案 2(生产环境): 在生产环境中部署新代码的正确方法是在文件更新后重置 OPcache
    • 通过代码: 您可以从 CLI 脚本或应用程序中安全、隐藏的 URL 调用 opcache_reset()
    • 通过重启: 重启您的 PHP-FPM 池或 Apache 模块。
    • 通过部署工具: 大多数现代部署工具(如 Deployer, Capistrano)都有钩子来运行 opcache_reset()

2. "我的缓存总是满的!"

您可能会在日志中看到警告,或者在 opcache_get_status() 中看到 opcache_hit_ratefalse

  • 解决方案: 增加 opcache.memory_consumption 和/或 opcache.max_accelerated_files

3. "脚本没有被缓存。"

检查 opcache_get_status() 的输出。查看 scripts 部分以了解实际缓存了什么。确保您的 opcache.memory_consumption 没有设置得过低。

总结:一个适用于生产环境的基础配置

以下是一个适用于生产服务器的可靠起始配置:

ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.revalidate_freq=300 ; 5 分钟
opcache.validate_timestamps=1 ; 如果您能做到,可以设置为 0 并手动重置缓存
opcache.fast_shutdown=1
; opcache.preload=/path/to/preload.php

总结来说,OPcache 不仅仅是一种优化;它是任何严肃的 PHP 部署中必不可少的组件。正确启用和调优它是您可以进行的最高影响力的性能更改之一。

学而不思则罔,思而不学则殆。