php数组的方便性,很容易令人过度使用,其中一种过度使用的场合,就是做缓存时不区分场合滥用数组,导致性能不高(主要是集中在对CPU性能的高占用消耗上[1])。以下以phpcms v9举例说明。
phpcms v9中,作为模块设计的pc标签[2]其实是不错的,易于让人理解而且功能强大,但偏偏在关乎效率的缓存上似乎没有意识到一个问题。绝大部分的开发者使用pc标签的缓存参数,其实质是希望将该pc标签包围的模块进行缓存,故而从缓存实现来讲,作为一个视图层而言,应该缓存最终渲染的html才对;但phpcms却只是缓存了数据数组,每次读取缓存并还原为数组后,仍然要进行渲染运算。这样的结果,导致了pc标签的缓存效率,其实没有设计者想象的那么高。
比如如下pc标签:
{pc:content action=”position” posid=”1″ thumb=”1″ order=”listorder DESC” num=”5″ cache=”500″}
<div class=”content” id=”main-slide”>
<div class=”changeDiv”>
{loop $data $r}
<a href=”{$r['url']}” title=”{str_cut($r['title'],30)}”><img src=”{thumb($r['thumb'],310,260)}” alt=”{$r['title']}” width=”310″ height=”260″ /></a>
{/loop}
</div>
</div>
{/pc}
其本意应该是为了缓存该pc标签包围的焦点图片模块。但是生成的最终代码发现,缓存的是中间从数据库读取出来的数组,仍需要每次渲染:
<?php if(defined(‘IN_ADMIN’) && !defined(‘HTML’)) {echo “<div class=\”admin_piao\” pc_action=\”content\” data=\”op=content&tag_md5=2e957216affd4b95207c8d8eabcfb7b8&action=position&posid=1&thumb=1&order=listorder+DESC&num=5&cache=500&htmlblockcache=1\”><a href=\”javascript:void(0)\” class=\”admin_piao_edit\”>编辑</a>”;}$tag_cache_name = md5(implode(‘&’,array(‘posid’=>’1′,’thumb’=>’1′,’order’=>’listorder DESC’,'htmlblockcache’=>’1′,)).’2e957216affd4b95207c8d8eabcfb7b8′);if(!$data = tpl_cache($tag_cache_name,500)){$content_tag = pc_base::load_app_class(“content_tag”, “content”);if (method_exists($content_tag, ‘position’)) {$data = $content_tag->position(array(‘posid’=>’1′,’thumb’=>’1′,’order’=>’listorder DESC’,'htmlblockcache’=>’1′,’limit’=>’5′,));}if(!empty($data)){setcache($tag_cache_name, $data, ‘tpl_data’);}}?>
<div class=”content” id=”main-slide”>
<div class=”changeDiv”>
<?php $n=1;if(is_array($data)) foreach($data AS $r) { ?>
<a href=”<?php echo $r['url'];?>” title=”<?php echo str_cut($r['title'],30);?>”><img src=”<?php echo thumb($r['thumb'],310,260);?>” alt=”<?php echo $r['title'];?>” width=”310″ height=”260″ /></a>
<?php $n++;}unset($n); ?>
</div>
</div>
<?php if(defined(‘IN_ADMIN’) && !defined(‘HTML’)) {echo ‘</div>’;}?>
解决这种效率问题,最直接的方法是增加参数,允许改为存储整块最终渲染的HTML模块。为此,改造了phpcms,允许增加一个htmlblockcache=”1″参数(前提是cache参数必须大于0),以解决此问题:
{pc:content action=”position” posid=”1″ thumb=”1″ order=”listorder DESC” num=”5″ cache=”500″ htmlblockcache=”1″}
{/pc}
受条件所限,ab测试只能在本机实验。测试环境为:Win 2003 + IIS 6 + php 5.2,没有安装任何opcode缓存扩展。测试方法是:后台更新(删除)所有缓存后,在浏览器刷新首页一次,以便重新生成所需缓存;首次两轮-n 10 -c 10预热,接着两轮-n 600 -c 15正式测试,取第二次作对比。
测试结果发现,改造后的响应速度有提高不小。但是由于更新缓存时需要进行ob缓冲,其响应会偶尔偏高。
改造前(没有htmlblockcache)的首页测试结果:
Document Path: /phpcmsv9/index.php
Document Length: 41900 bytes
Concurrency Level: 15
Time taken for tests: 33.891 seconds
Complete requests: 600
Failed requests: 0
Write errors: 0
Total transferred: 25286400 bytes
HTML transferred: 25140000 bytes
Requests per second: 17.70 [#/sec] (mean)
Time per request: 847.266 [ms] (mean)
Time per request: 56.484 [ms] (mean, across all concurrent requests)
Transfer rate: 728.63 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 1.9 0 16
Processing: 156 843 361.4 906 1875
Waiting: 141 839 360.8 906 1875
Total: 156 843 361.5 906 1875
Percentage of the requests served within a certain time (ms)
50% 906
66% 1031
75% 1109
80% 1156
90% 1281
95% 1344
98% 1453
99% 1516
100% 1875 (longest request)
改造后(有htmlblockcache)的首页测试结果:
Document Path: /phpcmsv9/index.php
Document Length: 41328 bytes
Concurrency Level: 15
Time taken for tests: 23.156 seconds
Complete requests: 600
Failed requests: 0
Write errors: 0
Total transferred: 24943200 bytes
HTML transferred: 24796800 bytes
Requests per second: 25.91 [#/sec] (mean)
Time per request: 578.906 [ms] (mean)
Time per request: 38.594 [ms] (mean, across all concurrent requests)
Transfer rate: 1051.92 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 5.9 0 109
Processing: 94 570 177.5 547 2297
Waiting: 94 563 177.7 547 2297
Total: 94 571 177.3 547 2297
Percentage of the requests served within a certain time (ms)
50% 547
66% 594
75% 625
80% 641
90% 703
95% 813
98% 1016
99% 1266
100% 2297 (longest request)
而实际服务器也支持这种测试结果——由于响应速度的提高,phpcgi的线程数也少了,CPU占用的叠加程度有所减少,负荷有所减轻。