php7性能优化之opcache

鸟哥在博客中说,提高PHP 7性能的几个tips,第一条就是开启opcache:

记得启用Zend Opcache, 因为PHP7即使不启用Opcache速度也比PHP-5.6启用了Opcache快, 所以之前测试时期就发生了有人一直没有启用Opcache的事情。

背景

最近业务有所增长,随之而来的是慢请求逐渐多了起来,在搜索php性能优化的过程中发现了opcache,相关的文章很多,但是都比较零碎,所以在此做个总结。公司当前使用的PHP版本为php7.x.x。

opcache是什么?

Opcache 的前生是 Optimizer+ ,它是PHP的官方公司 Zend 开发的一款闭源但可以免费使用的 PHP 优化加速组件。

官网介绍:

OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能, 存储预编译字节码的好处就是 省去了每次加载和解析 PHP 脚本的开销。

PHP 5.5.0 及后续版本中已经绑定了 OPcache 扩展。 对于 PHP 5.2,5.3 和 5.4 版本可以使用 » PECL 扩展中的 OPcache 库。

PHP的正常执行流程如下:

img

request请求(nginx,apache,cli等)–>Zend引擎读取.php文件–>扫描其词典和表达式 –>解析文件–>创建要执行的计算机代码(称为Opcode)–>最后执行Opcode–> response 返回。

如上图,启用opcache之前,每一次请求PHP脚本都会执行一遍以上步骤,如果PHP源代码没有变化,那么Opcode也不会变化,显然没有必要每次都重新生成Opcode,结合在Web中无所不在的缓存机制,我们可以把Opcode缓存下来,以后直接访问缓存的Opcode岂不是更快。

启用Opcode缓存之后的流程图如下所示:

img

Opcode cache 的目地是避免重复编译,减少 CPU 和内存开销。

如何使用opcache?

载入opcache扩展

PHP 5.5及后续版本默认都绑定了opcache扩展,所以我在此就不需要再编译安装扩展了,可以直接编辑 php.ini 文件配置载入 opcache 扩展。

载入方法,在php.ini文件加入:

zend_extension=opcache.so

然后重启,php-fpm服务,通过命令可查看PHP当前支持的扩展:

$ php -m
[Zend Modules]
Zend OPcache

启用opcache

至此PHP已加载opcache模块,然后就需要进行修改 php.ini 配置,启用 opcache (以下是官方推荐配置,仅供参考):

[opcache]
opcache.enable=1
opcache.use_cwd=1
opcache.enable_cli=1
opcache.save_comments=1
opcache.huge_code_pages=1
opcache.memory_consumption=128
opcache.max_wasted_percentage=5
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=40960
opcache.validate_timestamps=0
opcache.force_restart_timeout=180
opcache.error_log=/usr/local/php-fpm/var/log/opcache.log
opcache.log_verbosity_level=1

重启php-fpm服务后生效。

关于Linux内核HugePage

在opcache配置中有这样一个参数:huge_code_pages=1,该参数需要配合系统HugePage参数共同使用。

关于Hugepage详细介绍可参考:Linux HugePage 特性

在系统中开启Hugepage:

# 分配512个预留的大页内存
$ sysctl vm.nr_hugepages=512 

# 查看内存信息
$ cat /proc/meminfo | grep Huge

优化效果

未使用opcache:

img

使用opcache:

img

很显然,使用opcache后效果还是很明显的,请求处理明显快了很多!

opcache参数详解

;opcache模块配置
[opcache]
opcache.enable=1 (default "1")
;OPcache打开/关闭开关。当设置为Off或者0时,会关闭Opcache, 代码没有被优化和缓存。

opcache.enable_cli=1 (default "0")
;CLI环境下,PHP启用OPcache。这主要是为了测试和调试。从 PHP 7.1.2 开始,默认启用。

opcache.memory_consumption=128 (default "64")
;OPcache共享内存存储大小。用于存储预编译的opcode(以MB为单位)。

opcache.huge_code_pages=1
;启用或者禁用将 PHP 代码(文本段)拷贝到 HUGE PAGES 中。 此项配置指令可以提高性能,但是需要在 OS 层面进行对应的配置。

opcache.interned_strings_buffer=16 (default "4")
;这是一个很有用的选项,但是似乎完全没有文档说明。PHP使用了一种叫做字符串驻留(string interning)的技术来改善性能。
;例如,如果你在代码中使用了1000次字符串“foobar”,在PHP内部只会在第一使用这个字符串的时候;分配一个不可变的内存区域来存储这个字符串,其他的999次使用都会直接指向这个内存区域。这个选项则会把这个特性提升一个层次,
;默认情况下这个不可变的内存区域只会存在于单个php-fpm的进程中,如果设置了这个选项,那么它将会在所有的php-fpm进程中共享。
;在比较大的应用中,这可以非常有效地节约内存,提高应用的性能。
;这个选项的值是以兆字节(megabytes)作为单位,如果把它设置为16,则表示16MB,默认是4MB,这是一个比较低的值。

opcache.max_accelerated_files=40960 (default "2000")
;这个选项用于控制内存中最多可以缓存多少个PHP文件。这个选项必须得设置得足够大,大于你的项目中的所有PHP文件的总和。
;设置值取值范围最小值是 200,最大值在 PHP 5.5.6 之前是 100000,PHP 5.5.6 及之后是 1000000。也就是说在200到1000000之间。
;你可以运行“find . -type f -print | grep php | wc -l”这个命令来快速计算你的代码库中的PHP文件数。

opcache.max_wasted_percentage=5 (default "5")
;计划重新启动之前,“浪费”内存的最大百分比。

opcache.use_cwd=1 (default "1")
;如果启用,OPcache将在哈希表的脚本键之后附加改脚本的工作目录,以避免同名脚本冲突的问题。禁用此选项可以提高性能,但是可能会导致应用崩溃

opcache.validate_timestamps=0 (default "1")
;如果启用(设置为1),OPcache会在opcache.revalidate_freq设置的秒数去检测文件的时间戳(timestamp)检查脚本是否更新。
;如果这个选项被禁用(设置为0),opcache.revalidate_freq会被忽略,PHP文件永远不会被检查。
;这意味着如果你修改了你的代码,然后你把它更新到服务器上,再在浏览器上请求更新的代码对应的功能,你会看不到更新的效果,你必须使用 `opcache_reset()` 或者 `opcache_invalidate()` 函数来手动重置OPcache。或者重重你的web服务器或者php-fpm 来使文件系统更改生效。
;我强烈建议你在生产环境中设置为0,why?因为当你在更新服务器代码的时候,如果代码较多,更新操作是有些延迟的,在这个延迟的过程中必然出现老代码和新代码混合的情况,这个时候对用户请求的处理必然存在不确定性。最后,等所有的代码更新完毕后,再平滑重启PHP和web服务器。

;opcache.revalidate_freq=2 (default "2")
;这个选项用于设置缓存的过期时间(单位是秒),当这个时间达到后,opcache会检查你的代码是否改变,如果改变了PHP会重新编译它,生成新的opcode,并且更新缓存,只有当opcache.validate_timestamps=1生效。
;值为“0”表示每次请求都会检查你的PHP代码是否更新(这意味着会增加很多次stat系统调用,译注:stat系统调用是读取文件的状态,这里主要是获取最近修改时间,这个系统调用会发生磁盘I/O,所以必然会消耗一些CPU时间,当然系统调用本身也会消耗一些CPU时间)。可以在开发环境中把它设置为0,生产环境下不用管。

;opcache.revalidate_path=0 (default "0")
;在include_path优化中启用或禁用文件搜索
;如果被禁用,并且找到了使用的缓存文件相同的include_path,该文件不被再次搜索。因此,如果一个文件与include_path中的其他地方相同的名称出现将不会被发现。
;如果此优化对此有效,请启用此指令你的应用程序,这个指令的默认值是禁用的,这意味着该优化是活跃的。

opcache.error_log=/usr/local/php-fpm/var/log/opcache.log
;opcache日志存储位置

opcache.log_verbosity_level=1
;opcache日志级别 0 fatal errors, 1 errors,2 warning, 3 info, 4 debug

opcache.save_comments=1
;如果禁用,脚本文件中的注释内容将不会被包含到操作码缓存文件, 这样可以有效减小优化后的文件体积
;建议开启,禁用此配置指令可能会导致一些依赖注释或注解的 应用或框架无法正常工作, 比如: Doctrine, Zend Framework 2 以及 PHPUnit。

opcache.force_restart_timeout=180
;如果缓存处于非激活状态,等待多少秒之后计划重启。 如果超出了设定时间,则 OPcache 模块将杀除持有缓存锁的进程, 并进行重启。

;opcache.file_cache=/tmp
;配置二级缓存目录并启用二级缓存。 启用二级缓存可以在 SHM 内存满了、服务器重启或者重置 SHM 的时候提高性能。 默认值为空字符串 "",表示禁用基于文件的缓存。

更多配置参数见:运行时配置

opcache管理——cachetool

相对于启用opcache,日常运维人员更关心的是如何对其进行管理。

CacheTool通过CLI管理和查看(当然,你还可以通过phpinfo来查看)APCu、opcache、file-cache的状态。

下载即用:cachetool

示例:

php cachetool.phar apcu:cache:info --fcgi
php cachetool.phar apcu:cache:info --fcgi=127.0.0.1:9000
php cachetool.phar opcache:status --fcgi=/var/run/php5-fpm.sock
php cachetool.phar opcache:status --fcgi=/var/run/php5-fpm.sock --fcgi-chroot=/path/to/chroot --tmp-dir=/path/to/chroot/tmp
php cachetool.phar opcache:status --cli
php cachetool.phar opcache:status --web --web-path=/path/to/your/document/root --web-url=http://url-to-your-document.root
php cachetool.phar opcache:status --web=SymfonyHttpClient --web-path=/path/to/your/document/root --web-url=http://url-to-your-document.root
apcu
  apcu:cache:clear            Clears APCu cache
  apcu:cache:info             Shows APCu user & system cache information
  apcu:cache:info:keys        Shows APCu keys cache information
  apcu:key:delete             Deletes an APCu key
  apcu:key:exists             Checks if an APCu key exists
  apcu:key:fetch              Shows the content of an APCu key
  apcu:key:store              Store an APCu key with given value
  apcu:regexp:delete          Deletes all APCu key matching a regexp
  apcu:sma:info               Show APCu shared memory allocation information
 opcache
  opcache:compile:script      Compile single script from path to the opcode cache
  opcache:compile:scripts     Compile scripts from path to the opcode cache
  opcache:configuration       Get configuration information about the cache
  opcache:invalidate:scripts  Remove scripts from the opcode cache
  opcache:reset               Resets the contents of the opcode cache
  opcache:reset:file-cache    Deletes all contents of the file cache directory
  opcache:status              Show summary information about the opcode cache
  opcache:status:scripts      Show scripts in the opcode cache
 stat
  stat:clear                  Clears the file status cache, including the realpath cache
  stat:realpath_get           Show summary information of realpath cache entries
  stat:realpath_size          Display size of realpath cache
CacheTool PHP
9.x >=8.1
8.x >=8.0
7.x >=7.3
6.x >=7.3
5.x >=7.2
4.x >=7.1
3.x >=5.5.9
2.x >=5.5.9
1.x >=5.3.3

img

如果php-fpm限制了监听地址,需要在 --fcgi 中指定,如 --fcgi=192.168.1.110:9000 (同php-fpm.conf的listen配置),可以通过 netstat -lntp |grep php-fpm 查看。

更多学习参考