OPcache 有什么用?
OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能, 存储预编译字节码的好处就是 省去了每次加载和解析 PHP 脚本的开销。
OPcache如何开启?
参考手册:http://php.net/manual/zh/opcache.installation.php
OPcache相关设置?(本文主要针对windows上的配置)
关于配置的说明和更多的配置项参考:http://php.net/manual/zh/opcache.configuration.php
OPcache使用(OPcache函数)
1、 opcache_get_configuration () 获取缓存的配置信息
opcache_get_status () 获取缓存的状态信息
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
<?php echo '<pre>' ; // 获取缓存的配置信息 var_dump(opcache_get_configuration()); echo '<hr/>' ; // 获取缓存的状态信息 var_dump(opcache_get_status()); echo '</pre>' ; /** * opcache_get_configuration和opcache_get_status返回的都是一组有关缓存配置信息 * 类似于上面的输出 array(3) { ["directives"]=> array(24) { ["opcache.enable"]=> bool(true) ["opcache.enable_cli"]=> bool(true) ["opcache.use_cwd"]=> bool(true) ["opcache.validate_timestamps"]=> bool(true) ["opcache.inherited_hack"]=> bool(true) ["opcache.dups_fix"]=> bool(false) ["opcache.revalidate_path"]=> bool(false) ["opcache.log_verbosity_level"]=> int(1) ["opcache.memory_consumption"]=> int(134217728) ["opcache.max_accelerated_files"]=> int(4000) ["opcache.max_wasted_percentage"]=> float(0.05) ["opcache.consistency_checks"]=> int(0) ["opcache.force_restart_timeout"]=> int(180) ["opcache.revalidate_freq"]=> int(60) ["opcache.preferred_memory_model"]=> string(0) "" ["opcache.blacklist_filename"]=> string(0) "" ["opcache.max_file_size"]=> int(0) ["opcache.error_log"]=> string(0) "" ["opcache.protect_memory"]=> bool(false) ["opcache.save_comments"]=> bool(true) ["opcache.load_comments"]=> bool(true) ["opcache.fast_shutdown"]=> bool(true) ["opcache.enable_file_override"]=> bool(false) ["opcache.optimization_level"]=> int(2147483647) } ["version"]=> array(2) { ["version"]=> string(5) "7.0.3" ["opcache_product_name"]=> string(12) "Zend OPcache" } ["blacklist"]=> array(0) { } } array(7) { ["opcache_enabled"]=> bool(true) ["cache_full"]=> bool(false) ["restart_pending"]=> bool(false) ["restart_in_progress"]=> bool(false) ["memory_usage"]=> array(4) { ["used_memory"]=> int(231368) ["free_memory"]=> int(133986360) ["wasted_memory"]=> int(0) ["current_wasted_percentage"]=> float(0) } ["opcache_statistics"]=> array(13) { ["num_cached_scripts"]=> int(2) ["num_cached_keys"]=> int(4) ["max_cached_keys"]=> int(7963) ["hits"]=> int(0) ["start_time"]=> int(1429153705) ["last_restart_time"]=> int(1429161635) ["oom_restarts"]=> int(0) ["hash_restarts"]=> int(0) ["manual_restarts"]=> int(81) ["misses"]=> int(2) ["blacklist_misses"]=> int(0) ["blacklist_miss_ratio"]=> float(0) ["opcache_hit_rate"]=> float(0) } ["scripts"]=> array(2) { ["D:WWWphp-codeopcacheoptest.php"]=> array(6) { ["full_path"]=> string(34) "D:WWWphp-codeopcacheoptest.php" ["hits"]=> int(0) ["memory_consumption"]=> int(4656) ["last_used"]=> string(24) "Thu Apr 16 13:20:35 2015" ["last_used_timestamp"]=> int(1429161635) ["timestamp"]=> int(1429161630) } ["D:WWWphp-codeopcacheopcache_config.php"]=> array(6) { ["full_path"]=> string(42) "D:WWWphp-codeopcacheopcache_config.php" ["hits"]=> int(0) ["memory_consumption"]=> int(2080) ["last_used"]=> string(24) "Thu Apr 16 14:28:32 2015" ["last_used_timestamp"]=> int(1429165712) ["timestamp"]=> int(1429150438) } } } */ |
2、下面我们将进行一些练习示例,注意所有的这一切都需要你确定你已经开启了php的OPcache扩展
去掉php.ini文件中的OPcache前面的分号
完成后重启Apache服务。
现在你将会发现:当我们像平时一样修改php代码以后刷新页面,页面并不会立即显示出我们的变化,好了来段代码说明:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?php $stime = microtime(); try { // 这里我们简单的写了一个PDO连接数据库的测试程序 $dsn = "mysql:host=localhost;dbname=test" ; $pdo = new PDO( $dsn , 'root' , 'root' ); $sql = "SELECT * FROM account" ; $stmt = $pdo ->prepare( $sql ); $stmt ->execute(); // 假设第一次我们只输出了有多少行 echo $stmt ->rowCount(); } catch (PDOExecption $e ){ $e ->getMessage(); } |
这里显示有 6 条数据,
如果我们此时想打印这 6 条数据,我们像平时那样,简单修改一下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?php $stime = microtime(); try { // 这里我们简单的写了一个PDO连接数据库的测试程序 $dsn = "mysql:host=localhost;dbname=test" ; $pdo = new PDO( $dsn , 'root' , 'root' ); $sql = "SELECT * FROM account" ; $stmt = $pdo ->prepare( $sql ); $stmt ->execute(); // 假设第一次我们只输出了有多少行 echo $stmt ->rowCount(); echo '<hr/>' ; // 我们想要打印查询出的所有数据 print_r( $stmt ->fetchAll()); } catch (PDOExecption $e ){ $e ->getMessage(); } |
我们现在刷新页面发现,
咦? 怎么没有变化,难道是我们写错了吗? 如果你确定自己的代码没有问题,那么问题来了?这是为什么?是Apache疯了还是php疯了,还是我自己疯了?
其实如果你对自己的代码有信心也有耐心,多刷新几次,就会发现,结果出来了
好奇怪?有木有,其实不是这样了,由于我们上面修改php.ini文件已经开启OPcache,当我们第一次运行这个脚本的时候,php会检查->翻译->运行 我们写的这个脚本,和以前是一样的,但是我们开启了OPcache,这次php会将翻译好的php中间代码:字节码opcode加载到内存中,当再次修改我们的脚本运行时,其实运行的是内存中的php中间代码opcode,我们修改过的脚本并没有运行
那为什么过一会,又显示正确了呢?
还是我们上面 OPcache 的设置
看看解释:
1
2
|
opcache.revalidate_freq integer 检查脚本时间戳是否有更新的周期,以秒为单位。 设置为 0 会导致针对每个请求, OPcache 都会检查脚本更新。 |
所以就是每60秒php会检查我们的脚本是否被修改过,如果修改过那就重新执行正常的那一套:检查->翻译->运行,然后把中间代码加载到内存中,
所以在上面 我们的修改不会跟往常一样刷新页面就行,需要等待一会。这样是不是对缓存很有感受啊:我们修改了文件,却跟没有修改一样 ,页面没有显示任何变化 0.0 !
3、opcache_reset() 重置字节码缓存的内容
对于我们上面出现的这种情况,如果我们不想每次都等待60秒,我们就可以使用这个方法,这个方法的定义是:在调用 opcache_reset() 之后,所有的脚本将会重新载入并且在下次被点击的时候重新解析。
注意一旦调用这个方法,所有的脚本都会被重新解析,然后再次载入到内存,记住是所有脚本,如果我们只是针对某个脚本的话,我们可以考虑使用:opcache_invalidate — 废除脚本缓存 (下面再说这个)
下面我们新建两个文件:opecho.php和opreset.php,简单的几行代码做个示例
1
2
3
4
5
6
7
8
9
|
<?php /* * filename: opecho.php * author : wangxb * create : 2015_04_17 02:44:16 * update : @update */
|
然后假设我们此时,又想echo一些内容
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?php /* * filename: opecho.php * author : wangxb * create : 2015_04_17 02:44:16 * update : @update */ echo "这是第一次修改,这个脚本会和以前一样去解释执行,然后将翻译好的中间代码opcode载入到内存中,<br>当我们在一分钟之内再次修改文件输出是没有变化的" ; // 假设此时我们想在输出一些内容 echo "<hr/>" ; echo "这是第二次输出" ; |
我们发现,修改代码后没有变化
然后我们执行 opreset.php
opreset.php
1
2
3
4
5
6
7
8
9
|
<?php /* * filename: opreset.php * author : wangxb * create : 2015_04_17 02:44:25 * update : @update */
echo opcache_reset() ? '成功重置缓存内容' : '重置失败' ; |
刷新我们刚才的页面
修改出现了,如果你觉得你在60内完不成这么多操作,无法出现按我说的这样的情景,可以把 opcache.revalidate_freq 这个参数设置大一些 比如 180 秒,这样的话 会每隔三分钟才去坚持一次文件是否更新过
4、opcache_invalidate ()
现在我们再新建一个opinvalidate.php 文件,再新建一个opecho1.php文件,利用我们上面的opecho.php文件,这次我们只想重新解释执行opecho1.php中的修改,
1
2
3
4
5
6
7
8
|
<?php /* * filename: opecho1.php * author : wangxb * create : 2015_04_17 10:44:20 * update : @update */ echo '我们将使用php中的opcache_invalidate()方法,让这个文件的字节码opcode手动失效' ; |
此时我们执行文件页面
此时我们同时修改opecho.php和opecho1.php这两个文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?php /* * filename: opecho.php * author : wangxb * create : 2015_04_17 02:44:16 * update : @update */ echo "这是第一次修改,这个脚本会和以前一样去解释执行,然后将翻译好的中间代码opcode载入到内存中,<br>当我们在一分钟之内再次修改文件输出是没有变化的" ; // 假设此时我们想在输出一些内容 echo "<hr/>" ; echo "这是第二次输出" ; echo '<hr/>修改了opecho1.php文件' ; |
1
2
3
4
5
6
7
8
9
10
|
<?php /* * filename: opecho1.php * author : wangxb * create : 2015_04_17 10:44:20 * update : @update */ echo '我们将使用php中的opcache_invalidate()方法,让这个文件的字节码opcode手动失效' ; echo '<hr/>修改了opecho1.php文件' ; |
然后刷新页面,页面没有变化:
当我们执行opinvalidate.php
1
2
3
4
5
|
<?php // opcache_invalidate() 方法中有两个参数: // args: 1、 缓存需要被作废对应的脚本路径 // 2、 布尔型,true表示 强制作废, false或者缺省值,表示文件更新后才失效 echo opcache_invalidate( './opecho1.php' , TRUE) ? '操作成功' : '操作失败' ; |
我们就会看到:刷新 opecho1.php 页面
我们的修改出现了,再来看看opecho.php页面
没有变化,明白? 我们运行opinvalidate.php页面时,opecho1.php 脚本在缓存中的opcode被废弃了,php会重新执行一次opecho1.php这个脚本,所以我们看到了 我们的修改
5、opcache_compile_file() 方法:无需运行,即可编译并缓存 PHP 脚本
我们新建两个脚本:opcompile.php和compileScript.php,我们在opcompile.php中执行OPcache_compile_file方法缓存compileScript.php脚本
1
2
3
4
5
6
7
8
9
10
11
|
<?php /* * filename: opcomplie.php * author : wangxb * create : 2015_04_17 11:53:30 * update : @update */ // opcache_compile_file() 方法要求传入待缓存文件的路径,此时不需要运行这个带缓存文件,只需要执行这个方法即可在内存中缓存 echo opcache_compile_file( './compileScript.php' ) ? '脚本成功缓存' : '脚本缓存失败' ; |
1
2
3
4
5
6
7
8
9
|
<?php /* * filename: compileScript.php * author : wangxb * create : 2015_04_17 12:44:16 * update : @update */ echo '这是脚本compileScript.php' ; |
现在执行 compile.php文件,让他去把compileScript.php脚本加入到缓存中
然后我们修改:compileScript.php
1
2
3
4
5
6
7
8
9
10
11
|
<?php /* * filename: compileScript.php * author : wangxb * create : 2015_04_17 12:44:16 * update : @update */ echo '这是脚本compileScript.php' ; echo '我们第一次修改compileScript.php文件' ; |
现在来在浏览器中看看这个脚本:
没有变化,那是因为,这次运行的时候,执行的是我们上面执行compile.php文件时在内存中生成的第一次的compileScript.php的中间代码
6、opcache_is_script_cached()
这个方法看名字估计都能猜出他是什么意思了:检查一个脚本是否在OPcache缓存中
我们也来一个示例:
1
2
3
4
5
6
7
8
9
10
|
<?php /* * filename: opisscriptcache.php * author : wangxb * create : 2015_04_17 13:14:16 * update : @update */ // 我们就以前面已经在加载到内存中的opecho.php文件作为测试对象 echo opcache_is_script_cached( './opecho.php' ) ? 'opecho.php脚本中间代码已经缓存' : 'opecho.php没有缓存中间代码opcode在内存中' ; |
很不幸,出了一个很严重的错误,
没有这个方法,难道是我们错了吗?
其实不是了,是因为这个方法要求:
看看我们的PHPinfo
所以用不了,其他的为什么都好着? 其他的方法要求进步都是v7.0.0、v7.0.2 我们这个满足,所以其他方法都可以使用,既然这样,这个方法也就这么说说算了,很简单的一个方法,平时也就是测试使用
切记:最后如果你测试完了,还是应该关闭OPcache,这样才不至于被OPcache捣乱我们平时的开发工作
结语
好了,关于php缓存 OPcache的使用就介绍到这里,php还有一个扩张APC也是针对缓存的,我们下一篇再做说明,只用缓存来说,这个面就很大了,不光是这里说的php的缓存,数据库数据缓存、页面缓存等等。所以目前打算先把php中间OPcache层面的缓存弄清楚
如果你不知道什么是opcode字节码、中间代码,给你推荐一篇博客:http://blog.linuxeye.com/361.html
OK、就先这样吧,这段时间被换工作的事情搞得有点烦,上周面试了一家公司,不是专门做开发的公司,估计是那种做做页面一两个人维护的简单网站或者写一些简单的微信开发什么的,其实我不是开不起这样的公司,这家公司给我还不错的待遇,可是我是过不了心里那一道坎:幻想能找到一个技术氛围好的开发公司,自己能够见识到更多、更深、更广。自己也能在技术上有更大的提高,可是心里又告诉自己,难道一辈子要当个程序员吗?应该为以后想想,不一定要技术成长,工资待遇可以就行了。可是自己还是过不了心里那颗技术的梦,还是放不下在技术道路上狂奔的想法。哎、纠结,只能写写博客缓解一下这么纠结的心情了,让自己静静
上面的代码在本人的GitHub上: https://github.com/wxb/php-code/tree/master/opcache