昨天《如何衡量单机PHP支撑能力》提到了OPcache,今天在自己的ECS上做了些测试,结果令人激动。
即使不使用OPcache,PHP7的性能也是非常高的。PHP脚本每次运行的时候,都要动态解析PHP代码,然后再执行。
如果将解析的结果保存下来,这样执行的时候就省去了解析,执行的性能就会提升。
将代码解析结果保存到共享内存中,这就是OPcache,PHP-FPM worker进程都能使用该内存区域。
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。
具体见 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_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。