文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>php性能调节 拷自 Programming PHP中文版

php性能调节 拷自 Programming PHP中文版

时间:2007-03-03  来源:lib

13.5  性能调节
Performance Tuning
在考虑更多关于性能调节的问题
之前,请把时间花在让你的代码正常工作上面。有了正确的可以工作的代码,你才可以定位其中运行缓慢的部分(或者称为“瓶颈”)。如果你尝试在编写代码的过
程中就优化它,你会发现那些优化过的代码变得难以阅读,并且通常需要花更多的时间来编写它。如果你花时间在并没有真正出现问题的代码上,那是在浪费时间。
到你维护那些代码的时候,你已经不能读懂它了。

图13-1:显示出错信息,而不是缓存的HTML
一旦你可以让你的代码正常工作之后,你可能会发现它需要一些优化。优化代码的目的主要在于两个方面:缩短代码执行时间和减少内存占用。
在你开始优化之前,问问你自己
到底需不需要优化。有太多的程序员把时间浪费在当页面每5分钟被浏览一次时,一系列复杂的字符串函数调用比一个Perl正则表达式快还是慢上了。
优化代码只在当一个页面需要太长时间来载入,用户感觉它比较慢时才是必需的,而这个问题通常是一个非常流行的站点才有的——如果页面请求进入服务器的速度
足够快,则从生成该页面的时间可以看出立即发送和服务器过载的区别。如果你的站点让用户长时间地等待,可以打赌你的用户会马上决定访问另一个站点来获得信
息。
一旦你决定了要优化(最好要经
过终端用户的测试和观察来作决定),必须精确了解到底什么因素导致了程序运行缓慢。你可以使用“性能测试”一节中介绍的技术来计算你的页面中各种子程序和
逻辑单元的执行时间。从结果你可以推测哪些部分花费了最多时间,而这些部分就是你应该集中你的精力去优化的地方。如果页面的生成需要花费5秒钟,你不可能
通过优化一个总共只占用0.25秒的函数来优化它,使时间降到2秒钟。你要分辨出最浪费时间的代码段并专注于它们。请测定你要优化的页面和代码段的执行时
间,以确保你对代码的修改起到了正面的而不是反面的作用。
13.5.1  性能测试(Benchmarking)
Benchmarking
如果你正在使用Apache服务器,你可以用Apapche性能测试工具ab来进行高层次的性能测试。要使用它,请运行:
$ /usr/local/apache/bin/ab -c 10 -n 1000
http://localhost/info.php
该命令访问PHP脚本info.php 1000次,并在任何时刻都保持10个并发的请求。该性能测试工具返回和测试相关的各种信息,包括最快、最慢和平均的载入时间。你可以将这些值和一个静态HTML页面进行比较来确定到底你的脚本运行得有多快。
例如,这是对一个仅调用phpinfo()的页面进行1000次抓取的输出结果:
This is ApacheBench, Version 1.3d  apache-1.3
Copyright (c) 1996 Adam Twiss, Zeus Technology
Ltd,
http://www.zeustech.net/
Copyright (c) 1998-2001 The Apache Group,
http://www.apache.org/
Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Finished 1000 requests
Server
Software:        Apache/1.3.22
Server
Hostname:        localhost
Server
Port:            80
Document
Path:          /info.php
Document
Length:        49414 bytes
Concurrency Level:     
10
Time taken for tests:   8.198 seconds
Complete requests:     
1000
Failed
requests:        0
Broken pipe errors:     0
Total transferred:     
49900378 bytes
HTML
transferred:       49679845 bytes
Requests per second:    121.98
[#/sec] (mean)
Time per
request:       81.98 [ms] (mean)
Time per
request:       8.20 [ms] (mean, across all
concurrent requests)
Transfer
rate:          6086.90
[Kbytes/sec] received
Connnection Times (ms)
            
min  mean[+/-sd] median   max
Connect:      
0    12   16.9     
1    72
Processing:   
7    69   68.5     58  
596
Waiting:      
0    64   69.4     50  
596
Total:         
7    81   66.5     79  
596
Percentage of the requests served within a
certain time (ms)
  50%     79
  66%     80
  75%     83
  80%     84
  90%    158
  95%    221
  98%    268
  99%    288
100%    596 (last request)
如果你的PHP脚本使用session,你从ab中得到的结果将不能真实反映现实中脚本的性能。因为一个session会在
一个请求过程中被锁定,运行ab得到并发请求的结果将不具说服力(我们实际应该测试的是10个不同用户对某个页面同时访问的数据,而不是一个用户对某个页
面同时访问10次的数据)。因为现实中程序运行时,session会与单个用户联系起来,而一个用户一般不太可能制造并发请求。
使用ab可以告诉你你的页面的整体速度,但是它并不能给你有关页面里独立函数的代码块的速度信息。如果你试图提高页面的访问
速度,可使用ab来测试你对代码的修改效果——我们会在下一个小节向你展示如何测定页面中某个部分的执行时间。但是从根本上讲,如果整个页面载入和运行仍
然很慢,这些局部测试并不会有太大意义。你的性能优化成功的最终证明还是来自于ab报告的数据。
13.5.2  性能监测(Profiling)
Profiling
PHP本身并没有内建的性能监测器(profiler),但是你可以使用一些技术来考察你认为有性能问题的代码。其中一个技
术就是调用mircrotime()
函数来得到消耗时间的精确值。你可以将你要进行性能监测器的代码用microtime()包围起来,然后使用mirotime()的返回值来计算该代码的
耗费时间。
例如,你可以用这段代码来计算phpinfo()函数生成输出要花费多长时间:
ob_start(  );
$start = microtime(  );
phpinfo(  );
$end = microtime(  );
ob_end_clean(  );
echo "phpinfo(  ) took " .
($end-$start) . " seconds to run.\n";
?>
刷新几次这个页面,你会看到数值有轻微的波动。如果非常频繁地
刷新页面,你会发现数值出现相当大的波动。只对一个代码片段运行一次来度量它的执行时间是危险的,其危险之处在于你可能并没有处于一个有代表性的服务器压
力环境下——服务器可能由于启动emacs而正在进行内存分页(paging),或者它可能把源代码从缓存中清除。得到执行时间精确值的最好方式是多次重
复测量执行时间,并计算这些时间的平均值。
PEAR的Benchmark类让反复测量脚本中的局部代码变得很容易。下面是一个简单的例子,向你展示了如何使用Benchmark类:
require_once 'Benchmark/Timer.php';
$timer = new Benchmark_Timer;
$timer->start(  );
sleep(1);
$timer->setMarker('Marker 1');
sleep(2);
$timer->stop(  );
$profiling = $timer->getProfiling(
);
foreach($profiling as $time) {
     echo $time['name'] . ':
' .  $time['diff'] . "
\n";
}
echo 'Total: ' . $time['total'] .
"
\n";
?>
这个程序的输出结果:
Start: -
Marker 1: 1.0006979703903
Stop: 2.0100029706955
Total: 3.0107009410858
也就是说,这段代码一共花了1.0006979703903秒运行到
marker 1,它是我们在sleep(1)调用后设置的,所以它就是你期待的结果。一共花了两秒多的时间从marker
1运行到结尾,并且整个脚本刚好花了3秒多的时间。如果你喜欢,可以添加任意个数的标记来测量脚本不同部分的运行速度。
13.5.3  优化执行时间
Optimizing Execution
Time
这里有一些技巧,可以帮助你缩短脚本的执行时间:
l         
如果echo可以满足你的需求,避免使用printf()
l         
避免在循环里面重新计算数值,因为PHP解释器不会移除循环中的不变的量。例如,如果$array的大小没有变化,请不要这么做:
for ($i=0; $i 相反,要像这样做:
$num = count($array);
for ($i=0; $i l         
只包含(include)你需要的文件,将要包含的文件切分成只包含要用到的函数。虽然这样代码可能会有一点难于管理,但可以降低不少代码解析时的开销。
l         
当一个简单的字符串操作函数可以满足需要时,不要使用正则表达式,例如,将字符串中的一个字符转换为另外的字符,使用str_replace(),而不是preg_replace()。
13.5.4  优化内存需求
Optimizing Memory
Requirements
这里有一些技巧,可以帮助你减少脚本的内存需求。
l         
尽可能使用数字代替字符串
for ($i="0"; $i for ($i=0; $i l         
使用完一个很大的字符串,请将容纳那个字符串的变量设置为一个空字符串。这样可以释放内存以便重新利用。
l         
只include或者require实际需要的文件。使用include_once或者require_once代替include和require。
l         
如果使用MySQL并且产生了很大的数据集,考虑使用MySQL专用的数据库扩展,你可以使用mysql_unbuffered_query()函数。该函数并不会立刻将整个数据集载入到内存中,而是在需要时逐行进行读取。
l         
当使用完MySQL或其他数据库的数据集后,尽快释放它们。在用完它们之后还在内存中维持这些数据是没有任何好处的。
13.5.5  反向代理和数据库同步
Reverse Proxies and
Replication
虽然添加硬件通常是获得更高性能的最快的方式,但最好还是先对你的软件进行性能测试,通常来说,修改软件比购买新的硬件成本更低。本节将讨论3种常见的针对高流量瓶颈的解决方案:反向代理缓存、负载平衡服务器和数据库同步。
13.5.5.1  反向代理缓存
反向代理(reverse proxy)是位于WEB服务器前的一个程序,它处理从客户端浏览器发来的所有连接。代理经过优化,可以快速处理静态页面。大多数的动态站点都可以在一小
段时间内缓存起来而不会影响服务。通常,你要用web服务器之外的一台独立的机器来运行反向代理程序。
举个例子,假设有一个繁忙的站点,它的首页每秒钟被访问50次。如果首
页页面中包含两个数据库查询,并且数据库更新的频率是一分钟两次,那么通过使用Cache-
Control头信息告诉反向代理对页面缓存30秒,每分钟你可以避免5994次数据库查询。这样做最坏的结果是从数据库更新到用户,看到这个新的信息有
30秒的延迟。 对于大多数应用来说,30秒并不是很严重的延迟,而且给性能提升带来很大的益处(译注2)。
反向代理甚至可以智能地缓存那些根据浏览器类型、接受的语言或者类似特性进行个性化的或者定制的内容。典型的解决方案是发送不同的头信息给缓存,告诉它哪些请求的参数会影响缓存过程。
现在有不少可用的硬件实现的代理缓存,同时也有非常好的软件实现的代理缓存。想要一个高质量并且非常灵活的开源代理缓存,请参看Squid软件(
http://www.squid-cache.org
)。要得到更多关于代理缓存和如何调节站点的信息,请查看Duane Wessels编著的《Web Caching》(O'Reilly出版)。
13.5.5.2  负载平衡和重定向
提高性能的另一种方法是把负载分摊到一组机器上。一个负载平衡系统
(load-balancing
system)可以均匀地分配负载或者将传入的请求发送到负载最小的机器上。重定向器(redirector)则是一个对传入的URL地址进行重写的程
序,它可以精细控制分配请求到单个服务器的过程。
再次强调,虽然有硬件实现的HTTP重定向器和负载平衡器,但是用软件也同样可以高效地完成重定向和负载平衡。通过一些软件,如SquidGuard(http://www.squidguard.org),可以给Squid添加重定向逻辑模块,你可以做很多事情来提高性能。
13.5.5.3  MySQL同步
有时候数据库服务器是整个应用的瓶颈——大量并发请求可以让数据库服务
器陷入瘫痪状态,从而导致性能下降。同步(Replication,也称复制)是最好的解决方案之一,它让一个数据库中所有发生的事情很快地同步到一台或
更多其他的服务器上,这样你就得到了多个完全一样的数据库。这可以让你将查询分散到很多数据库服务器中,而不总是查询一台服务器。
最有效的模型是使用单向同步(one-way replication):系统中只有一台主数据库,将数据复制到几个从数据库。所有的数据库写操作(delete,update,insert)都在主数据库进行,数
据库读操作(即select)则平衡分布到多个从数据库上。这个技术针对于那些进行大量的读操作但是写操作很少的架构。大多数web应用程序正好适合这种情况。
图13-2展示了在同步过程中主数据库和从数据库之间的关系。

图13-2:数据库同步关系
很多数据库都支持同步,包括MySQL、PostgreSQL和Oracle。
13.5.5.4  综合运用
对于一个实际应用的高效的系统架构,我们需要综合应用以上提到的各种概念,来完成图13-3所示的配置。

图13-3:综合应用
我们使用5个独立的服务器——一个用于反向代理和重定向器,3个WEB服务器和1个主数据库。这个架构可以处理非常庞大数量
的请求,其精确数量仅仅取决于两个瓶颈——单个的Squid代理和单个的数据库主服务器。你可以试着做一些创新:将这两台服务器也切分成多个服务器。除此
之外,如果你的应用程序是可以缓存的并且主要是进行数据库读操作的,那么这样的系统架构将是一个相当完美的解决方案。
每一个Apache服务器有它自己的只读MySQL服务器。PHP程序中所有的数据库读请求都经过一个Unix-domain
的本地socket连接到专门的MySQL实例上。在这个系统框架下面,你可以加入你所需要的任何数量的Apache/PHP/MySQL服务器。任何来自PHP 脚本的数据库写操作请求将经过TCP socket到达MySQL的主服务器。   
               
               
               
               
               
               

相关阅读 更多 +
排行榜 更多 +
开心动动脑安卓版 v1.0 手机版

开心动动脑安卓版 v1.0 手机版

休闲益智 下载
不良人破局手游下载

不良人破局手游下载

角色扮演 下载
云海之下手游下载

云海之下手游下载

角色扮演 下载