昨天《如何衡量单机PHP支撑能力》提到了OPcache,今天在自己的ECS上做了些测试,结果令人激动。

什么是Opcache

即使不使用OPcache,PHP7的性能也是非常高的。PHP脚本每次运行的时候,都要动态解析PHP代码,然后再执行。

如果将解析的结果保存下来,这样执行的时候就省去了解析,执行的性能就会提升。

将代码解析结果保存到共享内存中,这就是OPcache,PHP-FPM worker进程都能使用该内存区域。

如何启用OPcache

PHP5.6以上的版本默认编译了OPcache,不过要真正启用,必须在php.ini中配置 zend_extension=opcache.so,动态加载 Zend extension。

如果php -v 出现with Zend OPcache v7.2.8, Copyright (c) 1999-2018, by Zend Technologies,说明启用了OPcache。

如何配置OPcache

具体见 www.php.net/manual/zh/opcache.configuration.php,说几个有用的。

opcache.enable 表示启用OPcache。

opcache.enable_cli 表示PHP命令行也启用OPcache。

opcache.memory_consumption 表示共享内存的大小。

opcache.interned_strings_buffer PHP相同字符串可以保存到共享内存中。

opcache.validate_timestamps 如果是1表示每次都会检查PHP文件的更新时间,以便确定是否使用OPcache,在生产环境中建议关闭,这样性能更高,那如果源文件修改了怎么办?后面会说。

opcache.max_accelerated_files 表示可缓存文件的最大个数。

opcache.enable_file_override 如果开启,则在调用函数 file_exists(),is_file() 以及 is_readable() 的时候,都会检查OPcache。

opcache.file_cache 可以将OPcache导出到外部文件,这样即使重启PHP,外部文件的OPcache也能使用,但自己没有开启。

如何运用在生产环境

OPcache确实很好,但如果opcache.validate_timestamps关闭,那么源文件如果变更,如何能够更新OPcache呢?

有两种方法,第一个就是重启PHP-FPM,第二个就是调用 opcache_reset() 函数。

重启PHP-FPM不太适合,比如在QA环境,不能修改一行代码就启动PHP-FPM,所以目前的方法就是QA代码(if/else)使用opcache_reset()强制不使用OPcache。

那么生产环境如何更新OPcache呢?目前的方法就是在ssh git分发代码的时候,增加curl一个接口,该接口就是执行opcache_reset()。

OPcache统计

内置opcache_get_status()函数可以了解统计数据,比如:

[memory_usage] => Array
(
    [used_memory] => 21200864
    [free_memory] => 113016864
    [wasted_memory] => 0
    [current_wasted_percentage] => 0
)

[interned_strings_usage] => Array
(
    [buffer_size] => 8388608
    [used_memory] => 710928
    [free_memory] => 7677680
    [number_of_strings] => 16899
)

[opcache_statistics] => Array
(
    [num_cached_scripts] => 36
    [num_cached_keys] => 44
    [max_cached_keys] => 16229
    [hits] => 36
    [start_time] => 1591457444
    [last_restart_time] => 1591457446
    [oom_restarts] => 0
    [hash_restarts] => 0
    [manual_restarts] => 1
    [misses] => 36
    [blacklist_misses] => 0
    [blacklist_miss_ratio] => 0
    [opcache_hit_rate] => 50
)

[scripts] => Array

free_memory 表示共享内存还有多少没有用;current_wasted_percentage表示浪费的内存,达到一定比例,会自动清除OPcache;opcache_hit_rate表示OPcache命中率;scripts 表示查看那些脚本被缓存了。

具体效果

使用ab进行并发测试。

yum -y install httpd-tools
ab -n 10000 -c 500 url #并发500

在开启OPcache的时候:

Concurrency Level:      500
Time taken for tests:   38.155 seconds
Complete requests:      10000
Failed requests:        340
   (Connect: 0, Receive: 0, Length: 340, Exceptions: 0)
Write errors:           0
Non-2xx responses:      340
Total transferred:      1938420 bytes
HTML transferred:       564000 bytes
Requests per second:    262.09 [#/sec] (mean)
Time per request:       1907.740 [ms] (mean)
Time per request:       3.815 [ms] (mean, across all concurrent requests)
Transfer rate:          49.61 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       14  510 1156.1     25   15056
Processing:    19  398 1557.7     32   29043
Waiting:       19  398 1557.7     32   29043
Total:         33  908 2014.5    269   32053

没有开启的情况下:

Concurrency Level:      500
Time taken for tests:   28.447 seconds
Complete requests:      10000
Failed requests:        7663
   (Connect: 0, Receive: 0, Length: 7663, Exceptions: 0)
Write errors:           0
Non-2xx responses:      10000
Total transferred:      2784615 bytes
HTML transferred:       1270593 bytes
Requests per second:    351.53 [#/sec] (mean)
Time per request:       1422.361 [ms] (mean)
Time per request:       2.845 [ms] (mean, across all concurrent requests)
Transfer rate:          95.59 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       14  326 1302.2     23   15060
Processing:    14 1035 1884.1     27    9045
Waiting:       14 1035 1884.1     27    9045
Total:         29 1361 2246.7     58   21169

有的同学很奇怪,怎么开启OPcache的情况下,执行时间反而更长?原因在于并发数太高,而单请求响应时间长,PHP-FPM worker子进程达到了500;而开启OPcache的时候,执行效率高,worker进程没有超过20,所以其总体时间更长。

另外如果没有开启OPcache,则CPU负载瞬间飙到10以上;而开启OPcache的话,CPU负载没有超过1。

相关文章