PHP性能优化之Opcache

Opcache全程Operation Code, 意为操作码。由于PHP是解释型语言,当解析器执行PHP脚本时会解析脚本代码,将它们生成可以直接运行的中间代码,称为Zend OpCode, 类似于Java的ByteCode。

Zend引擎

opcache

Zend引擎是PHP的编译引擎和执行引擎,当它执行一段PHP脚本时,会依次按照如下步骤执行:

  • Scan:扫描,将PHP代码转换成语言片段。
  • Parse:解析,将语言片段组合成有意义的表达式。
  • Complie:编译,将表达式编译程OpCode。
  • Excute:执行,顺次执行OpCode。

Nginx或其他Web服务器把HTTP请求转发给PHP-FPM, PHP-FPM再把请求交给某个PHP子进程处理,PHP进程找到PHP脚本后执行,把脚本编译为OpCode后生成响应。我们从图中看到PHP脚本被编译为Zend Opcode后生成内容,然后被发送到浏览器客户端。

如果每次请求一个PHP脚本都要编译一次Zend OpCode,然后执行字节码,就会消耗很多资源。如果每次HTTP请求PHP都必须不断解析、编译和执行PHP脚本,消耗的资源更多。如果有一个工具能缓存预告编译好的字节码,减少应用的响应时间,降低系统资源压力,这当然就是我们想要的方式—字节码缓存。

字节码缓存的共通特性就是能够存储预先编译的Zend OpCode,使用OpCode缓存后,当请求一个PHP脚本时,不用再读取、解析和编译PHP代码。PHP解释器会从内存中读取预先编译好的字节码,立即执行。这样就能节省很多时间,极大提升应用的性能。

opcache

Zend Opcache

在PHP5.5以后,Zend OpCache虽然被内置,但默认没有启用,需要显式指定启用Zend OpCache。

如果是自己编译的PHP运行环境,需要在configure命令时包含如下:

–enable -opcache

编译好,须在php.ini文件中配置Zend OpCache。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
zend_extension=opcache.so
[opcache]
;开启opcache
opcache.enable=1

;CLI环境下,PHP启用OPcache
opcache.enable_cli=1

;OPcache共享内存存储大小,单位MB
opcache.memory_consumption=128

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

;这个选项用于控制内存中最多可以缓存多少个PHP文件。这个选项必须得设置得足够大,大于你的项目中的所有PHP文件的总和。
设置值取值范围最小值是 200,最大值在 PHP 5.5.6 之前是 100000,PHP 5.5.6 及之后是 1000000。也就是说在200到1000000之间。
opcache.max_accelerated_files=4000

;设置缓存的过期时间(单位是秒),为0的话每次都要检查
opcache.revalidate_freq=60

;从字面上理解就是“允许更快速关闭”。它的作用是在单个请求结束时提供一种更快速的机制来调用代码中的析构器,从而加快PHP的响应速度和PHP进程资源的回收速度,这样应用程序可以更快速地响应下一个请求。把它设置为1就可以使用这个机制了。
opcache.fast_shutdown=1

;如果启用(设置为1),OPcache会在opcache.revalidate_freq设置的秒数去检测文件的时间戳(timestamp)检查脚本是否更新。
如果这个选项被禁用(设置为0),opcache.revalidate_freq会被忽略,PHP文件永远不会被检查。这意味着如果你修改了你的代码,然后你把它更新到服务器上,再在浏览器上请求更新的代码对应的功能,你会看不到更新的效果
强烈建议你在生产环境中设置为0,更新代码后,再平滑重启PHP和web服务器。
opcache.validate_timestamps=0

;开启Opcache File Cache(实验性), 通过开启这个, 我们可以让Opcache把opcode缓存缓存到外部文件中, 对于一些脚本, 会有很明显的性能提升.
这样PHP就会在/tmp目录下Cache一些Opcode的二进制导出文件, 可以跨PHP生命周期存在.
opcache.file_cache=/tmp

重启PHP FPM,使用phpinfo函数查看,确认Zend OpCache是否正常工作,如图所示:

opcache2

A/B测试

现在我们对开启Opcache效果进行A/B测试,以请求phpinfo.php为例。

未开启:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Document Path:          /phpinfo.php
Document Length: 94760 bytes

Concurrency Level: 100
Time taken for tests: 39.205 seconds
Complete requests: 10000
Failed requests: 880
(Connect: 0, Receive: 0, Length: 880, Exceptions: 0)
Total transferred: 949419025 bytes
HTML transferred: 947599025 bytes
Requests per second: 255.07 [#/sec] (mean)
Time per request: 392.054 [ms] (mean)
Time per request: 3.921 [ms] (mean, across all concurrent requests)
Transfer rate: 23648.95 [Kbytes/sec] received

未开启状态下,TPS:255/s。

开启后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Document Path:          /phpinfo.php
Document Length: 99540 bytes

Concurrency Level: 100
Time taken for tests: 35.199 seconds
Complete requests: 10000
Failed requests: 9041
(Connect: 0, Receive: 0, Length: 9041, Exceptions: 0)
Total transferred: 997228816 bytes
HTML transferred: 995408816 bytes
Requests per second: 284.10 [#/sec] (mean)
Time per request: 351.995 [ms] (mean)
Time per request: 3.520 [ms] (mean, across all concurrent requests)
Transfer rate: 27666.80 [Kbytes/sec] received

开启后,TPS: 284/s, 性能提升11.4%, 开启后效果还是非常棒的。

有用就打赏一下作者吧!