【原】Zend Framework Performance Guide (性能指南) 第一部分

 

最近正在进行针对Zend Framework(本文简称为ZF)的调研工作,本文翻译于ZF最新的官方手册附录E - Zend Framework Performance Guide。水平有限,多有缺点,请多指正。

原文链接:http://framework.zend.com/manual/en/performance.html

我的个人博客链接:http://becoder.cn/?p=119

E.2. Class Loading

任何对ZF应用进行性能分析都会意识到,由于Class loading所导致的性能问题是非常严重的。从大量的class文件载入到那些class-file不能一对一关联的plugin组件。各种各样的include_once和require_once充斥着我们的应用程序,从而导致了的大量的潜在问题,本章节我们提供了一些常见问题的具体解决方案。

E.2.1.  如何优化 Include_path ?

include_path的定义会Class loading的速度略有影响,通常我们可以通过以下四种方式来提高速度:

  1. 使用绝对路径引用(包括基于绝对路径的相对引用)
  2. 减少path的定义数量.
  3. 让ZF的include_path优先级越高越好
  4. 使当前目录在include_path中处于最后的位置,也就是’.’在最后(默认是最先)

E.2.1.1.  使用相对路径

绝对还是相对,这看上去并不像是一个大问题。它们之间的区别在于,如果我们使用相对引用,那么意味着程序无法使用PHP的realpath cache来快速访问文件目录,而绝对路径可以通过cache来提高我们的访问速度。

有两种非常方便的办法来实现绝对引用。第一种方式是我们在php.ini,httpd.conf, .htaccess或者其他的定义方式来通过硬编码的方式来使用绝对引用。其次我们可以在程序中通过PHP的realpath函数来帮助设置include_path为绝对引用。如下例:

$paths = array(
     realpath(dirname(__FILE__) . '/../library'),
     '.'
);
set_include_path(implode(PATH_SEPARATOR, $paths);

或者使用相对路径---当然,它还是相对于某个绝对路径(其实,它依然是绝对的)

define('APPLICATION_PATH', realpath(dirname(__FILE__)));

$paths = array(

    APPLICATION_PATH . '/../library'),

    '.',

);

set_include_path(implode(PATH_SEPARATOR, $paths);
 

E.2.1.2.  减少引用目录数量

在应用程序的解释过程中,扫描引用目录的顺序是由目录在include_path中定义的顺序所决定的。这样就意味着如果所需要引入文件的目录在include_path中定义的越优先,那么扫描引用目录的损耗就会越低。因此,我们可以通过减少引用目录的数量来提高速度。只需要谨慎得校对定义的include_path内容,去除那些无用的目录。

另一个优化措施是合并引用目录,例如,ZF采用了PEAR的命名规范,所以,当系统中需要引用PEAR类库或者其他采用PEAR命名规范的类库时,可以尝试着将这些类库放到同一个引用目录中。

E.2.1.3.  尽量提前引用 ZF 类库所在的 path

从第二点我们可以推断出第三点优化方案,把ZF所在目录定义在include_path最先位置,从而保证应用程序可以在第一次目录扫描中获取到ZF的相应文件。

E.2.1.4.  降低或排除当前目录引用

在很多出现include_path定义的例子中,都会在include_path中定义当前目录(‘.’),而且往往出现在include_path中的第一位(php.ini的默认值)。这的确为引用同一目录下文件提供了极大便利。但是这导致了应用程序会首先遍历当前目录来寻找文件,而在遵循ZF标准的应用程序中,大多数情况下我们根本不需要在当前目录下进行遍历。所以,请将’.’放到include_path的最后一位。

E.2.2.  如何去除不必要的 require 引入 ?

为了优化文件引入过程,PHP提供了延迟加载的优化措施,使用延迟加载,应用程序可以只在需要使用类的时候再进行类的加载,比如对象的初始化,静态方法调用以及静态变量或常量的引用时。PHP通过自动加载机制来实现延迟加载,我们可以通过定义一个或者多个回调函数来实现类名到文件名之间的映射。

然而,在我们的类库的代码中,依然存在并大量使用require_once调用(ZF目前正是如此)。所以现在我们需要停止使用这些require_once从而在最大限度上发挥autoloader所能带来的性能提升。

E.2.2.1.  通过 find sed 命令来停用 require_once

一个简单的办法是,我们可以通过UNIX的sed工具和find命令来注释掉每个require_once的调用。可以试着运行下面的命令来实现(%代表了shell提示符):

% cd path/to/ZendFramework/library

% find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' \

  -not -wholename '*/Application.php' -print0 | \

  xargs -0 sed --regexp-extended --in-place 's/(require_once)/\/\/ \1/g'
 

仅仅一行命令就遍历所有的php文件同时将每个’require_once’替代为’//require_once’。(我们必须保持在Zend_Application 和Zend_Loader_Autoloader文件中的require_once调用,不然会导致程序错误)

我们可以把这个处理命令加到自动编译或者部署流程中,从而提高我们的生产环境下的产品性能。同时需要注意的是,我们必需要将自动加载的过程加到public/index.php文件中,如下例:

require_once 'Zend/Loader/Autoloader.php';

Zend_Loader_Autoloader::getInstance();
 

E.2.3.  如何优化插件加载 ?

大部分组件都支持插件扩展,开发者可以通过自己创建类来和组件协同工作,或者重写已经存在的标准ZF自身插件。插件可以提高框架的灵活性,但代价是,插件载入也会导致性能问题。

插件加载器允许我们注册类前缀与路径的映射,以及在非标准路径下定义类。每个类前缀都可以对应多个路径,运行时,插件加载器会遍历所有的类前缀以及其所对应的所有的路径,检查路径下是否存在可供加载的文件。加载后系统会判断是否包含需要引用的类。我们可以想象一下,这个过程会导致多少次针对物理文件系统的I/O操作。

将你的结论乘以使用的PluginLoader的插件数量,我们可以得出关于插件性能问题的最终结论。在本文发布之时,使用PluginLoader包括以下组件:

  • Zend_Controller_Action_HelperBroker: helpers
  • Zend_Dojo: view helpers, form elements and decorators
  • Zend_File_Transfer: adapters
  • Zend_Filter_Inflector: filters (used by the ViewRenderer action helper and Zend_Layout)
  • Zend_Filter_Input: filters and validators
  • Zend_Form: elements, validators, filters, decorators, captcha and file transfer adapters
  • Zend_Paginator: adapters
  • Zend_View: helpers, filters

现在,我们应该如何来减少如此多的调用次数呢?

E.2.3.1.  使用 PluginLoader 引用文件缓存

ZF 1.7版本中为PluginLoader增加了引用文件缓存机制,这个功能将include_once调用保存到一个文件中,并在bootstrap过程中引用这个文件。虽然这样在我们代码中增加了多余的include_once调用,但同时能保证PluginLoader能够尽早的返回结果,减少引入过程的资源消耗。

E.3. Zend_Db 性能相关

作为数据库抽象层,Zend_DB类库封装了常见SQL操作;Zend_Db_Table则是数据表访问接口,为表级数据库操作提供常用抽象。在这些抽象和所带来的便利背后,有时也会导致额外的性能开销

E.3.1. 如何降低由于获取元数据所导致的性能开销?

为了简便起见,同时在开发过程中可以和库表结构保持变更同步,Zend_Db_Table在内部通过以下方式进行操作,即首次使用库表元数据时,从数据库获得元数据同时保存在成员变量以备之后操作使用。这种方式对性能存在较大影响,无论您底层使用何种数据库,都会导致系统瓶颈的出现。

好消息是,我们有一些方法来解决这个问题:

E.3.1.1. 使用元数据缓存

Zend_Db_Table可以通过使用Zend_Cache来缓存库表元数据。显然这要比每次从数据库中加载元数据要更快更实惠。

可以通过这个链接获得更多关于元数据缓存的内容 Zend_Db_Table documentation includes information on metadata caching .

E.3.1.2. 将元数据硬编码

从ZF 1.7开始,Zend_Db_Table支持可以将库表元数据硬编码到代码中。这属于一种”高级”选项:它的优缺点都是显而易见的,首先可以对性能带来毋庸置疑的提升,同时也意味着提高维护成本,你必须保证你的库表更改与代码中的设定同步。

E.3.2.  Zend_Db_Select所生成的SQL语句总是无法准确得命中库表索引,如何提高?

总体来说,Zend_Db_Select很好得完成了它的设计初衷,但是,当面对一些复杂的SQL查询时,如多表关联或者子查询时,它往往会体现得力不从心。

E.3.2.1. 直接使用自定义优化过的SQL语句

这个问题的正解是自己来写SQL。我们并不需要Zend_Db_Select为我们完成所有的事情。自己设计并且调优出的SQL语句是最正确最值得信赖的选择。

通过执行EXPLAIN命令来分析你的查询(Mysql only?),同时进行反复的测试直到你可以确认语句可以最大限度得发挥你的索引优势,然后将语句定义为成员变量或者类常量来使用它。

如果语句需要传递多个占位符参数,可以通过使用vsprintf() and array_walk()函数来将参数注入到SQL语句中。如下例:

// $adapter is the DB adapter. In Zend_Db_Table, retrieve
// it using $this->getAdapter().
$sql = vsprintf(
    self::SELECT_FOO,
    array_walk($values, array($adapter, 'quoteInto'))
);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值