PHP Opcache 注意事项以及调优

从 PHP5.5 开始,Opcache 扩展是核心的一部分,增加了对 PHP 脚本的字节码缓存的支持。对于动态语言(例如 PHP ),字节码缓存可以显著的提高性能,因为它可以确保脚本仅被编译一次。

Opcache 扩展的默认设置已经在很大程度上提高了 PHP 的性能,但是您可以通过修改默认配置以获取更佳的性能。

警告:这篇文章反复提到的监视统计信息  opcache_get_status(false)。因为 Opcache 为每个 SAPI 使用唯一的共享内存池,所以您无法从控制台访问 Web 服务器统计信息。该调用必须通过 Apache 或 PHP-FPM 进行。

当内存太小时, 应避免一些不正确的缓存方式

Opcache 默认使用 64 MB 内存来保存编译后的 PHP 脚本以及最多 3907 个 PHP 脚本。 虽然这足以缓存您的 PHP 脚本,但有一些需要注意的:

  • 如果您的应用会生成 (PHP) 代码或者使用基于 PHP 文件的缓存(例如: Symfony, Doctrine Annotations, FLOW3), 那么可能会有大量 PHP 文件不会被 Opcache 缓存

  • 如果您开启了 validate_timestamps 时间戳验证并在生产环境中修改了代码, 那么旧的缓存条目将被标记为过期, 这种过期的缓存条目会增加 Opcache 的内存消耗

  • 如果您使用每个版本部署到新目录的部署策略, 则 Opcache 会将不同目录下的同一个 PHP 文件进行多次缓存, 这样也会快速的占用 Opcache 的内存使用

当Opcache在某些情况下内存满时,它将擦除所有缓存条目并从空缓存开始。 如果发生这种情况,并且您的服务器流量很大,这可能会导致thundering herd problem或缓存猛击:许多请求同时生成相同的缓存条目。

你要避免Opcache用空的缓存重新启动。

检测opcache是否“已满”的算法取决于三个不同INI设置之间的直观交互,目前尚无记录。 它有助于深入理解C代码来理解它。

三个相关的ini变量定义如下:

-opcache.memory_consumption默认为64(MB)以缓存所有已编译的脚本。
-opcache.max_accelerated_files默认为2000个可缓存文件,但由于未记录的原因,在内部将其增加到较高的质数。 最大值为100.000
-opcache.max_wasted_percentage是Opcache中触发重启所需的空间浪费百分比(默认为5)。

Opcache基于先到先得的原则进行缓存,并且使用诸如最近最少使用(LRU)之类的驱逐策略。

每当达到最大内存消耗或最大加速文件时,Opcache就会尝试重新启动缓存。但是,如果Opcache中浪费的内存没有超过max_wasted_percentage,则Opcache将不会重启,并且每个请求都会重新编译每个未缓存的脚本,就像没有可用的opcache扩展一样。

这意味着您要避免Opcache内存被占满。

为了找到正确的配置,您应该监视opcache_get_status(false)的输出,该输出可用于检查内存,浪费和已用缓存键的数量:

-如果cache_full为true并且重启未进行或者不正在进行,则可能意味着浪费不足以超过最大浪费百分比。

您可以通过比较current_wasted_percentage和INI变量opcache.max_wasted_percentage来进行检查。在这种情况下,缓存命中率opcache_hit_rate也会降至> = 99%以下。
解决方案:增加 opcache.memory_consumption 配置。

  • 如果 cache_full 的值为 true ,同时 num_cached_keys 的值 与 max_cached_keys 相同,且你的文件很多。当没有足够的浪费时,则不会触发重新启动。造成的结果就是,即使可能有可用内存,有些脚本也不会被缓存。

    解决方案:增加 opcache.max_accelerated_files 配置。

  • 如果你的缓存永远不会满,但是你仍然看到有大量的重新启动。当你浪费太多或将最大浪费百分比配置得太低时,可能会发生这种情况。

    解决方案:增加 opcache.max_waste_percentage 配置。

要查找无效的重启行为,你可以评估 oom_restarts (与  opcache.memory_consumption 相关联) 和 hash_restarts(与 opcache.max_accelerated_files 相关联)配置。确保使用 last_restart_time 统计信息检查上次重启的时间。

要为 opcache.max_accelerated_files 找到一个好的参数值,你可以使用下面这行 Bash 命令获取当前项目中 PHP 文件数量:

$ find project/ -iname *.php|wc -l
9607

确保代码生成账户、缓存文件和多个应用程序运行在同一个 FPM 工作池中。

通过禁用时间戳验证来避免不必要的文件系统调用

使用默认设置时,无论何时执行 PHP 文件(比如通过 includerequire), Opcache 都会检查上次在磁盘上对其进行修改的时间。 然后,将这次与上次缓存该脚本的编译时间进行比较。 当缓存文件修改的时候,将重新生成脚本的编译缓存。

当你知道文件永不更改时,在生产中无需进行此验证。 要禁用时间戳验证,请将下面这行添加到你的 php.ini 中:

opcache.validate_timestamps=0

使用此配置,你必须确保在部署期间,所有缓存均无效。 有几种方法可以确保发生这种情况:

  • 重启 PHP FPM 或 Apache - 在 Opcache 中使用使文件无效的 sledgehammer 方法的不利之处在于,它可能导致请求中止,并在很小的时间内丢失请求。

  • 调用 opcache_reset() ,这很棘手,因为必须在由 Apache 或 PHP-FPM 执行的脚本中调用它才能影响 Web 服务器的 Opcache 。你可以将特殊端点添加到你的应用程序,并使用秘密哈希将其保护。

  • 变改文档根目录并重新加载 Nginx Web 服务器配置。Nginx 完成所有当前正在运行的请求,并并行启动新工作程序,以新配置为请求提供服务。

  • 对于 Apache,Rasmus 在 Etsy 上有一篇 博客文章 详细介绍了他们的方法。

仔细考虑后两种方法,可用的 Opcache 内存会非常快地“满”。 有关内存配置如何工作的更多信息,请参见上一节。

对于这两种情况,你可能都需要一个维护脚本,该维护脚本在旧部署中的所有脚本上调用 opcache_invalidate($file, true) 或直接 opcache_reset()。 否则,旧的部署会挤占新的缓存,你可能最终会获得完整的缓存和未加速的应用程序。

放到一起

如果要将所有结果组合在一起,则要在生产环境中配置以下用于 opcache 的 php.ini 配置:

opcache.memory_consumption=128 # MB,根据自己的需要调节
opcache.max_accelerated_files=10000 # 根据自己的需要调节
opcache.max_wasted_percentage=10 # 根据自己的需要调节
opcache.validate_timestamps=0

我们在 Tideways 演示应用程序服务器上对此进行了测试,该服务器使用“完整”的 opcache 运行,因为那里的 PHP-FPM 中运行着许多不同的 PHP 应用程序。演示应用程序显示了以下重大改进,因为现在所有脚本都适合 opcache 。在 Tideways 演示上注册以亲自直接查看这些事件 。

比较 Opcache 配置优化前后的 PHP 性能

比较 Opcache 配置优化前后的 PHP 性能

我们还测试了性能分析器本身的 Opcache 配置。在以下这种情况下,缓存尚未满,但是启用了时间戳验证。

PHP

比较 Opcache 配置优化前后的 PHP 性能

通过比较发行前后的 180,000 个请求,我们可以确定,大约 2 毫秒的改进可以归因于禁用的时间戳验证。

结语

即使 Opcache 已经大大提高了 PHP 性能,但是我们仍然可以改进它。本博客扩展了 php 文档中关于 opcache_get_status  和 opcache configuration 使用的一些细节.

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://tideways.com/profiler/blog/fine-...

译文地址:https://learnku.com/php/t/34638

本帖已被设为精华帖!
本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!