这篇文章实际上是一个读书笔记,为了锻炼自己的翻译能力,所以我把和Shcema设计时需要考虑的关键句子先用英文列出来,然后翻译在下面,最后结合自己工作中的体会有一个译者注,英文原文在附件里。要说明的是,本人以前为未做过类似翻译的工作,英文水平一般,所以在翻译过程中有些用词比较别扭和晦涩。请大家多包涵。促使我写这篇笔记的原因是,前段时间修改CQ应用程序并Upgrade到正式库后,本地
用户反应性能有下降,但不是很明显,在可接受的范围之类 。由于我们有异地
开发,并且未使用
IBM推荐的
ClearQuest MultiSite和Web方式,性能的下降甚至是10倍级而无法忍受的。深刻体会
ClearQuest Schema设计在性能方面的作用,但在网上这方面
资料非常少,或者几乎没有,后找到的这篇英文
资料可能很多人在设计中也未注意到,因为如果没有异地
开发性能下降的教训,只做本地
开发,
数据量还不大的情况下,一般是不会考虑到性能的。根据这篇
文档,我重新修改了设计,性能立刻就提升了。
Rational ClearQuest Performance Optimization
Regardless of how you will be deploying Rational ClearQuest– Windows or Unix clients, a Web Server, or a combination of several interfaces –your level of application performance will depend largely on how you address the following three key areas:
1. Schema Design Considerations
2. Server Configuration (Database and IIS Servers)
3. Network Configuration
We’ll discuss each of these in the following sections。
翻译:无论你怎么部署ClearQuest----Windows平台
客户端,Unix平台
客户端,还是Web
服务器,或者是它们的混合。CQ 应用程序性能水平很大程度上依赖于你怎么实现以下三个关键的部分。
1、 Schema设计考虑;
2、 服务器配置(
数据库和IIS服务器)
3、 网络架构
我们会在下面的章节里讨论这三点。
译者注:CQ应用程序从三个方面考虑,本篇笔记只翻译和关注第一点Schema设计需考虑到的地方。由于在工作中主要我们现在主要在Windows平台Clients端使用,所以主要关注的是与这部分有关的性能调优,对使用Web的应用程序未考虑。对于此详细可查看:
http://members.cox.net/ejostrander/clearteam/cq_howto.html#MISC14
Schema Design Considerations
This is probably the one area of your Rational ClearQuest deployment where you have the most control over how the application is going to perform. While Rational ClearQuest is a flexible and configurable change management system, it is not intended to be a complete program development environment. It is designed for customization of the “out of the box”
schemas, not for complete re-implementation of the underlying database application. If you find yourself writing thousands of lines of hook code in ClearQuest, you should probably look to simplify your business use cases instead of trying to capture and implement these complex rules in your hooks. The result will be a system that is much easier for you to deploy and maintain, and much more usable for your team members who are using the application to track defects and change requests. As a
schema designer, you will have many choices on how you implement the desired functionality in your
schema. In most cases, there is more than one way to achieve a particular result, and understanding the performance implications of using various constructs in Rational ClearQuest can help you make good decisions that will provide your users with the functionality they need with minimal impact to overall performance.
翻译:在部署CQ时,Shcema设计的考虑是你最可能提高应用程序的性能地方。由于CQ是一个灵活和可配置的变更
管理系统,而不是一个完整的程序开发环境。它的设计是基于基本的Schema的定制,而非一个数据库
系统的完全实现。如果你发现你在一个Hook里写了几千行
代码,你可能需要重新检查并简化你的业务用例,而非试图在HOOk去寻找和实现这些复杂的规则。这样做的结果是,CQ是一个更容易部署和维护的
系统,并且使用这个
系统作为bug追踪和变更
需求的团队成员来说更容易使用。作为一个Schema设计者,要在你的Schema里实现一个特定的
功能你有很多选择。在很多情况下,有不只一种方法可以实现一个特定的结果。理解在Rathional ClearQuest里使用不同的程序构造的性能含义,可以帮助你在提供给用户所需要的
功能和性能的最小影响之间做出更好的选择和判断。
译者注:这段文字是在做Schema设计时的一个大纲性质的论述,CQ不是一个完全的开发环境,它不过提供了一个环境可以让我们进行二次开发,我把它理解为一个轻量级的开发环境。在需要实现的功能需要大量的代码时,我们首先要做的是考虑是不是我们的业务规则有问题,
流程是否合理。然后在做设计时,要考虑性能和需实现的功能之间的平衡,在这篇文章中,多处使用了“Trade-Off”这个词,翻译过来就是两难选择,或者用通俗点话说就是两害相权取其轻。有时候为了性能考虑,我们不得不牺牲一些功能。在下面的文字中,将会看见我为了性能考虑,牺牲了一些代码的简洁和牺牲了一些原本已经实现的功能。
Rational ClearQuest Hooks – Performance Considerations
Regardless of your chosen scripting language, keep in mind that when a user logs in to Rational ClearQuest – whether it be through a native Windows or Unix client or through the
web interface – all hook code defined in the schema is loaded into memory on the CQ client (or CQWeb Server). More hook code results in longer login times and more memory consumption.
翻译:
无论你选择什么(VB,Perl,译者注)作为的
脚本语言,都需要记住,当用户登录时,不管是通过Windows或Unix平台的客户端还是通过Web
访问,所有在Shema中定义的Hook代码都会加载到CQ客户端(或CQ Web服务端),很多Hook 代码会引起登录需要更多的时间和更多的内存消耗。
译者注:这就是上面说的,如果要实现一个功能需要几千行代码的话,应该重新考虑业务用例。太多的代码会是登录时时间加长,并且消耗内存。
Also, ALL Record Scripts (for the current Record Type) and ALL Global Scripts are passed to the interpreter and compiled – EACH AND EVERY TIME an “action” is performed or a base action hook executes. It doesn’t matter if the executing hook actually uses any of the Record Scripts or Global Scripts – they are still passed to the interpreter. This can have performance implications, particularly if your schema contains significant amounts of code in Record Scripts and/or Global scripts. To minimize the amount of code sent to the interpreter, consider using PERL modules or VBScript COM objects – which can be loaded on an “as-needed” basis at execution time – to encapsulate your global scripts.
翻译:
同时,当每次执行“Action”或者一个Base Action Hook被执行时,所有的
记录脚本(当前的
记录类型)和所有全局脚本都会传给编译器和进行编译。无论这些执行的Hook是否使用这些
记录脚本或全局脚本,他们都会传给编译器。这个情况和性能有很大的关系,特别是如果你的Schema里包含较多重要的
记录脚本或者全局脚本时。为了使传给编译器的代码数量最小,考虑使用Perl模块或者VBSrcipt Com对象——他们能够在执行时按需加载,从而达到压缩全局脚本的目的。
译者注:这就是我在<<在CQ HOOK里发送带附件的
邮件>>里提到的,除非有必要,不要有冲动把有些函数写全局函数,在我以前的开发经验中,对于一些处理字符串或者数组的功能,如返回一个字符串里从左到右搜索某个子串左边部分,右边部分,从右到左搜索某个子串左边部分,右边部分。数组转化为字符串,判断某个元素是否属于某个数组等功能。我习惯于把这些写成一个个的函数在其他程序中调用,但是由于CQ的这个限制,很多时候只有牺牲代码的简洁,在CQ里写代码,没有办法把代码写得象诗,只能写得象记叙文。由于我们一定开发比较多,我写CQ的Global Scripts函数时的原则是,如果某个功能至少在其他代码里有5处的调用,我才抽出为一个全局函数。说实在的,这一点让人很郁闷。上面这段说用VBscript Com对象,问题是很多常用的功能还需要自己写成函数的。
Avoid using “AdminSession” to access data from the schema repository. User information is stored in the user database, so there is no need to go to the schema repository for data such as the user’s full name, etc. If you must retrieve data from the schema repository, store it in a Session Variable for reuse. (see example of using session variables below)
翻译:避免使用“AdminSession”对象从Schema repository库里取得数据,用户信息已经存到用户数据库,所以不需要从Schema repository数据库取得用户全名等信息。如果你真的需要从Schema repository数据库取数据,把这个数据存到Session变量里以遍重复使用。(请看下面的使用Session变量的例)
译者注:在以前的Hook中,我从来没有使用过AdminSession对象取数据,关于为什么不要使用AdminSession取数据,在我这篇文章开头提到那个网站上,Eric J. ostrander有论述:“If an AdminSession is started within a hook, CQ must load all the DLL's and assorted information into that session, which takes just as long as if somebody was starting the CQ Client from start”,从这里可以看出,如果使用了AdminSession对象,CQ需要加载所有的Dll
文件和分配各种信息到Session里,需要花费和登录一样的时间。
Avoid using “Recalculate Choice List” in your field properties. Doing so will force either a round-trip to the database or a hook script to run every time any field in the record is changed during an edit. Instead, write a “VALUE_CHANGED” hook in the “parent” field to force an update on the “child” choicelist values using the “InvalidateFieldChoiceList” and “SetFieldChoiceList” methods.
翻译:在
字段属性上避免使用Recalculate Choice List。这样做会产生这样的结果,当一条记录在编辑时,当任何一个
字段的值改变时都会读取数据库一个来回,并且运行hook脚本。替代的
解决方案是,在“父亲”
字段的“VALUE_CHANGED”hook里用“InvalidateFieldChoiceList” and “SetFieldChoiceList” methods方法来更新其子
字段的内容。
译者注:这是最需要注意的地方,实际上,我此次发生的性能下降问题主要就是这个原因引起,在Basic实现的Choice List属性上,由于对Recalculate Choice List性质了解不深,导致异地使用CQ慢了10个数量级,本地也有性能影响。在CQ中用Hook 实现Choice List时,应严格避免使用这个属性。这个属性的作用,举例说明,我的一个记录类型上有两个字段“
项目”和“模块”,其中“模块”列表依赖于用户在填写时选择的
项目的值。这个时候钩上这个属性很容易就实现了,但这个是以性能下降为代价的。IBM网站上有替代的选择是Dependent Choice Lists,在父字段的Value Changed 的Hook里写代码来实现。
Static/Dynamic Choice Lists should have <100 entries. This is because of cache limits in the Rational ClearQuest client. If you need more than 100 choices, use a REFERENCE_LIST.
翻译:静态或者动态的Choice Lists的条目应该小于100个。这是因为客户端的内存限制,如果你需要100个以上的Choices,用REFERENCE_LIST来实现。
译者注:在我写应用程序过程中, 没有使用过100个以上条目的Choice Lists,倒不是考虑到性能,而是如果有100个条目,估计整个CQ客户端界面都显示不完,应该很少有人这么做。但是,知道这个会影响性能也是必要的,这样以后我们在考虑功能实现时,可以更关注于性能。
Limit number of REFERENCE or REFERENCE_LIST fields displayed on a form. Rational ClearQuest must perform a “join” of all record types referenced by a form in order to render that form onscreen. Multi-table joins are expensive operations -- the time it takes to display a form will increase dramatically as the number of REFERENCE or REFERENCE_LIST fields to be displayed increases. A good rule of thumb is to have no more than 10 such fields on a single form.
翻译:限制在一个表单上REFERENCE 或REFERENCE_LIST字段的数量。在显示时这些关联的记录时,Rathional ClearQuest需要执行一个“加入”所有被这个表单关联的记录类型。Multi_Table的加入是耗时的操作,当一个表单上REFERENCE 或者REFERENCE_LIST的数量增加时,时间的消耗会急剧增加。首要的规则是在一个表单上不要有超过10个这样的字段。
译者注:REFERENCE 或REFERENCE_LIST字段过多的使用会使性能下降容易理解,但在以前的程序中也很少会在一个表单有10个这样的字段。一般来说,我不太喜欢用REFERENCE 或REFERENCE_LIST字段,原因是这种关联会导致在有时候要
删除数据时复杂性增加。
Avoid using FIELD_VALIDATION hooks. Field validation hooks are executed each time ANY field value changes during the course of an action. (It also increases the amount of hook code to be loaded into memory at login time.) Instead, validate individual field values in the ACTION_VALIDATION hook, as this is run only once when the user attempt to “Apply” (or commit) the changes made.
翻译:避免使用FIELD_VALIDATION hooks,当执行一个Action时,任何域值的改变都会导致Field validation hooks执行一次。(它同时也增加了在登录时加载到内存的代码数量),替代的解决方法是,在ACTION_VALIDATION hook里检查每一个域值的合法性,这样只有用户点“Apply”进行提交修改时才执行一次。
译者注:这个和“Recalculate Choice List“属性一样需要给予同样的注意,写FIELD_VALIDATION hooks代码会导致只要有任何一个域值变化都要去执行一遍这个合法性检查。如果写到ACTION_VALIDATION hook里,只有在提交时才执行一遍检查,这个也会导致数量级的性能问题。看看Eric J. ostrande的论述:“If performance is a problem and there are too many field Validation hooks, consider placing all the validations in an appropriate action hook. While the user doesn't get instant feedback if an incorrect value is entered, the improvement in performance may be worth that inconvenience.”,也就是说用这种方法来实现,确实给用户造成了不方便,它不是用户每改变一个值就马上检查字段值是否合法,而是等提交时才检查,但是,如果性能是你考虑的首要因素,这样的替代是值得的。这也是CQ需要考虑的一个“Trade-Off”。在我们现在的应用程序中,有很多使用了域值检查的这个是我下一个阶段改造的重点。但是也要注意,并非完全不能使用FIELD_VALIDATION hooks,只不过更需要经验。在我们面临一个Trade-off时,我们会选择一个我们更关注的。
Minimize the number of database queries executed by your hook code. Whenever possible, store the returned value of commonly used queries in a session variable. During that session, you can simply read the value instead of re-executing the query each time a hook is run. Do this only for queries that return fairly static data, such as user information, EntityType data, etc. If you use a session variable to store the return data from a query such as “all open defects owned by <user>”, you run the risk of that data being out of date on subsequent uses of the session variable. See below for an example of using a session variable to avoid a database query:
$sessionObj = $entity->GetSession();
if ($sessionObj->HasValue(“variable_name”))
{ $result = $sessionObj->GetNameValue(“variable_name”); }
else { # do whatever query is necessary to determine value… $session->SetNameValue(“variable_name”, $result);
}
翻译:在HOOk里,最小化查询数据的次数。只有有可能,应该把最常用的查询值记录到一个Session 变量里。当在这个Session期间,每次Hook执行时只需要去读取这个Sesssion变量的值而不是重新去执行查询。这样做只是对那些比较静态的数据来说,如用户信息,EntityType 数据或者其他。如果你用一个Session变量存储一个查询的返回数据如“当前用户的所有打开的bug”,你可能会以为这个Session变量持续地使用导致原来的数据过期的风险。请看下面使用Session变量来避免一个查询的例子。
译者注:这一点在调优中没有真正使用过,当然减少查询次数肯定会提高性能,尤其对于异地开发。
Avoid returning MULTILINE_TEXT fields in queries. With no multiline text fields, CQ will batch select 250 rows at a time from the database. So, a query returning N records will require roughly N/250 logical round-trips to the database. With multiline fields in the query result, CQ will fetch all non-multiline fields for ONE ROW, then do a separate fetch for each MULTILINE text field in that row. Then, it fetches the next row in the same way. So, a query returning N rows with M number of multiline fields in the result set will require (1+M)*N logical round-trips to the database. NOTE: A fix to address this issue is planned as a patch to v2003.06.00. We recommend avoiding the use of multiline text fields in your query result sets until this patch is installed.
翻译:避免在查询里返回MULTILINE_TEXT字段。没有multiline text字段,CQ会一次从数据库中批量选择250行。因此,一个返回N条记录的查询只需要N/250次客户端到数据库之间的逻辑往返查询。如果有multiline text字段,CQ会取一条记录的所有非multiline text字段为一行,然后取multiline text字段为一行。然后CQ用同样的方式再取下一条记录。因此,一个返回N行包含M个multiline text字段记录的查询需要(1+M)*N次逻辑往返查询。注意:关于这个问题的一个补丁包计划在V2003.06.00解决,在安装这个补丁包之前
建议不要使用返回multiline text字段的查询。
Use E-Mail Rules judiciously. Each e-mail rule generally contains a filter, and each filter is associated with a Query. Each time an e-mail rule is run, the associated query is executed. E-mail rules are loaded and run with every action. As an alternative to e-mail rules, you could use notification hooks (where you would need to write your own code to construct and send messages) which fire only on the appropriate actions. Try to minimize or combine e-mail rules to reduce the amount of query code executed. This may mean sending “generic” notifications to a wider audience instead of targeted e-mails to specific users or groups, but the performance trade-off may be worth it.
翻译:谨慎使用邮件规则,每一个邮件规则都包括一个过滤,每一个过滤和一个查询相联系。每一次邮件规则运行的时候,关联的查询也会执行。每一个Action的时候,邮件规则开始加载并开始运行。作为邮件规则的一个替代选择,可以使用Notification Hooks(在那里需要写代码来实现发送邮件),这样就可以在相应Action的时候才触发这个Hook。试着最小化和合并邮件规则以减少查询代码执行的数量。这意味着要给一个更广泛的用户范围发一封未定义的邮件。不过从性能上考虑是值得的。
译者注:我们现在使用的应用程序发送邮件都是通过Notification Hooks的方式,用邮件规则没使用过。
Don’t retrieve an entire record when a field or two will do. Retrieving an entire CQ record from the database is an expensive operation. If you only need one or two fields from a record or a collection of records, using a query to extract a result set containing just those fields is much more efficient than retrieving the record(s) and looping through those records to choose the desired field(s). Similarly, you can obtain the value of a field contained in a referenced record without retrieving the entire record first. For example, suppose the current entity has a field named “Project” that is a REFERENCE to another record type, which contains a field called “Name”. You can obtain the name of the referenced
project using: my $projname = $entity->GetFieldValue("Project.Name")->GetValue(); This will be more efficient than: my $
project = $entity->GetFieldValue(“Project”)->GetValue(); my $projname = $
project->GetFieldValue(“Name”)->GetValue();
翻译:但想去某个字段的值时候不要取出完整的记录。从CQ中取出一个完整的记录是很费时的操作。如果你只需要从一个记录或者记录集合里取一个或者两个字段的值,使用一个只查出这些字段的值作为结果的查询比取出整个记录或者循环查找记录集合取得想要的字段更有效率。同样的,你可以取的一个记录里关联字段的值而不需要完整地取出整个记录。如:假设当前的Entity有一个字段叫“Project”,这个字段REFERENCE到其他包含一个字段叫“name”的记录类型,用如下的代码,你可以得到关联的字段:
my $projname = $entity->GetFieldValue("Project.Name")->GetValue();
这样取值会比下面的代码更有效率:
my $project = $entity->GetFieldValue(“Project”)->GetValue(); my $projname = $project->GetFieldValue(“Name”)->GetValue();
译者注:这个比较容易理解,在客户端与数据库之间,应该尽量使传输的数据更少,如果取出整个记录,实际上传输的数据是该记录上所有的字段,相比只取出某个字段,性能上也是数量级上的差别。
其他1:在我上次做的性能调优的实践中,有一个操作在这个调优文档没有提到,在我们使用过程中,为了限制某些人员不能提交某种记录,在该记录类型的Submit里Access Control里用代码或者组来执行控制,但是,这个会产生一个结果是,功能上虽然限制了属于某个组的人员在点NewBug旁边的箭头时如果没有
权限提交的记录类型不会显示出来,但是代价是每个人在做这个操作时都会等更长的时间,原因是在做这个操作时要去判断CQ中所有记录类型的提交
权限。导致等待时间过长,所以后面又去掉了这个功能。这也是CQ里需要考虑的一个Trade-off.
其他2:下面是 Eric J. ostrande网站上的论述。
Reduce the size of the Submit form. Using a single form for Submit and Record will result in too much information being passed to the user during Submit. At the very least, take advantage of the ability in CQ to have a separate Submit form. At the most, only show fields absolutely necessary for the Submit action on the Submit form.
翻译:减少提交表单的大小,使用一个单独的表单来做提交和记录会产生在提交时很多信息传送到用户这里。最少,应利用CQ有分开的提交表的功能。更好的用法是,在单独的提交表上提交操作时只显示必要的字段。
译者注:这个在我们的设计中已经在使用了提交时不需要的字段不要显示在提交表单上面。也是从传送数据最小来考虑。
Reduce the number of users in a database. Avoid simply importing your entire domain into the CQ schema with everyone inactive and then activating only those users that need access to given databases. While this may be a very efficient for the CQ admin to populate user databases with user information, it puts too many useless records in the database. Domains can contain thousands of users, while only a few hundred actually need CQ access. While it's a little more work, find a way to parse the company's domain list to only relevant personnel prior to import.
翻译:减少数据库里的用户数量。避免简单的导入所有非
活动状态域用户进入CQ Schema,并且只是使部分用户有权限使用CQ。因为这样在数据库里产生很多无用的记录,使用CQ Admin帐户来管理用户数据库的用户会更有效率。域可能包括几千个用户,然后只有几白人可能使用到 CQ系统。需要花费一点时间去找到一个方法,解析公司域用户列表,只有相关的人员可以导入。
译者注:这个现在我们还没用到,如果要使用LDAP进行统一认证,就需要注意考虑这方面的问题。
[
本帖最后由 killer215 于 2007-2-3 20:22 编辑 ]