阅读 7

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 文件数量:


确保代码生成账户、缓存文件和多个应用程序运行在同一个 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 配置。在以下这种情况下,缓存尚未满,但是启用了时间戳验证。

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

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


结语

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

八重樱:怎么从一名码农成为架构师的必看知识点:目录大全(不定期更新)​
zhuanlan.zhihu.com图标

以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的可以加入我的官方群点击此处


关注下面的标签,发现更多相似文章
评论