IBM Lotus Notes 开发人员必看的性能基础知识

这份白皮书阐述对 IBM Lotus Notes 和 Domino 应用程序的性能造成巨大影响的最重要因素。它的主要目的是帮助 Notes 客户端应用程序开发人员识别问题,然后针对问题提供解决方案,从而实现最佳的性能。在这里,假设您已经知道如何创建 Notes 设计元素,以及知道如何对表单、字段...显示全部

这份白皮书阐述对 IBM Lotus Notes 和 Domino 应用程序的性能造成巨大影响的最重要因素。它的主要目的是帮助 Notes 客户端应用程序开发人员识别问题,然后针对问题提供解决方案,从而实现最佳的性能。

在这里,假设您已经知道如何创建 Notes 设计元素,以及知道如何对表单、字段、视图和列等组件设置选项。这份白皮书为各个级别的开发人员提供了学习材料。


简介

在 Lotus Notes 中开发简单的应用程序非常容易,并且在用户和文档的数量很少时,一般不会遇到性能问题。然而,只要应用程序是成功的,用户和数据的数量就会逐渐增多。如果在设计时没有考虑到性能问题,那么您的应用程序此时将会变得异常缓慢。

这份白皮书讨论影响 Notes/Domino® 应用程序的性能的主要因素,并且解释开发人员如何才能获得最佳的性能。这并不是一份详尽的指南;相反,我们主要关注最常见、最严重的设计问题。

这份白皮书的主要目的是帮助您识别 Notes 客户端应用程序的问题,并指导您找到解决办法。Web 应用程序也存在类似的设计问题,但它们的问题主要在另外两篇文章中解答,即 Appendices Group C of the IBM Redbooks 出版物 Performance Considerations
for Domino Applications 和 IBM Business Partner 文档 Performance Engineering Notes/
Domino Applications。


主要因素

一般而言,以下因素对应用程序的性能影响最大:

视图的数量及其复杂性。删除不使用的视图或合并相似的视图。对于包含相同文档但使用不同排序的视图,使用一个可重新排序的列合并它们。删除不需要的列,并简化选择和视图列公式。检查是否存在您不能访问的“服务器私有”视图或其他视图。在视图选择公式或列选择公式中使用 _cnnew1@Today 和 @Now。尽量避免这种情况。参见 IBM Support Web 站点技术文档 Time/Date views in Notes: What are the
options?;并阅读本文下面的视图小节。文档数量。文档越多打开的速度就越慢。可以考虑压缩旧文档或将主文档合并为单一文档。例如,如果您的主文档是一个“订单”,那么将订单上的每个“排列项”放到独立的文档中就是一个糟糕的做法。Lotus Notes 不是关系数据库,而是面向文档的数据库。储存在文档中的摘要字段的数量。不属于富文本的字段称为“摘要”字段(尽管这个称呼过于简单化)。文档包含的摘要字段越多,将其编入到视图索引中所需的时间就越长(如果存在几百个字段,那么所需的时间将增加 30%)。只要字段存在,即使不在视图中使用它们,也会造成一样的结果。有时使用更少的文档却需要更多的字段,反之亦然;必须仔细考虑才能为提升性能做出正确的选择。表单的复杂性。尝试将表单的数量限制为与实际需要的字段相等。表单越长,打开、刷新和保存它们所需的时间就会大大延长(并且视图索引器需要处理的字段也会增多)。修改文档。对文档进行不必要的修改会增加索引器的负担,从而降低了视图索引的速度,并且还会影响复制和全文本索引的速度。删除文档的数量。当删除一个文档时,就会留下一个称为“删除存根”的标记。复制程序需要根据这个标记决定是否从其他副本中删除相同的文档,或将“缺失”的文档复制到该副本。删除存根最终会过期(默认为 90 至 120 天),因此只要数据库保持正常的删除文档数量,就不会造成问题。

然而,我们见过一些应用程序包含的删除存根要比文档多好几倍。如果使用代理程序在夜间执行文档删除,然后从其他外部数据源创建新的文档,那么通常会出现这种情况。不要使用这种方法。您可以使用更高级的算法比较文档和源数据,从而确定应该更新或删除哪些文档。参见 Lotus Sandbox download 了解更多信息。

Reader 字段。如果有必要使用 Reader 字段,那么必须使用它 —— 没有其他方法能够提供它所提供的安全性。但要注意它对视图的性能造成的影响,尤其是用户仅访问大量文档中的一小部分文档时。这份白皮书的视图小节讲述了一些减小该字段的影响的技巧,另外 developerWorks 文章 Lotus Notes/Domino 7 application performance: Part 2: Optimizing database views 也讨论了类似的主题。用户数量。如果服务器上存在大量用户,那么将会影响应用程序和服务器的性能。当应用程序的性能已经处于临界状态时,添加新的用户会导致性能恶化。改良设计会有所帮助,但您还需要在其他服务器上创建副本(尤其是集群服务器),或鼓励用户创建快的本地副本。收起
参与10

查看其它 4 个回答的回答

公式性能

大部分 @ 函数都是相当快的,但有一小部分比较慢。因此要谨慎地使用它们:

@Contains 的开销还不是十分大,通常用于测试列表是否包含某个值,这是一种低效的方法。例如,如果 Cities 包含值“East Lansing”,则表达式 @Contains(Cities; “Lansing”) 返回 True。如果这正是您需要的,当然很好;但如果您查找的是包含“Lansing”值的条目,那么应该使用 =、*= 或 @IsMember。这些函数更加快,因为如果第一个字符不匹配,它们就不再扫描整个字符串。@For@While 通常可以被更高效的 @Transform 代替,或被其他对整个列表进行一次性操作的函数代替。@Unique 必须将列表中的每个值与其他值进行比较,因此执行时间与列表中的项数的平方成正比。对于其中的各个值都具有惟一性的列表,这个函数的表现会更好。后面还将对此进行讨论。@NameLookup 类似于 @DbLookup,但它仅用于目录信息。@DbLookup 和 @DbColumn。过度使用和错误使用这些函数是造成表单延迟的主要原因。下面将对此进行详细讨论。

我们通常不必要地使用了宏语言中的循环函数。尽管没有在这个 Domino
Designer 帮助文档中阐述宏函数,
但几乎所有接受字符串参数的宏函数都可以对列表进行操作。例如,@Left(x; “,”),其中 x 是一个列表,它返回一个所有元素都被“左置”的列表。

注意:在以前,@UserRoles@UserNamesList 函数都会造成严重的性能问题,但从 Lotus Notes 6.0 开始,这些函数的结果都将被缓存。

@DbLookup 和 @DbColumn

影响 @DbLookup 和 @DbColumn 的性能的 3 个主要因素是:

是否使用缓存正在查找的视图是否高效是否不必要地使用它们

使用缓存

许多开发人员过度地使用“NoCache”选项,尤其是在关键字公式中。这种现象很容易观察到,因为在开发和首次测试期间需要经常编辑关键字,因此NoCache(不使用缓存)是“正确的选择”。

然后,在应用程序投入使用之后,就不会经常编辑关键词。在出现新值时,迟一些再提供给用户可以得到更好的性能,这种代价是可以接受的。务必在必要时才使用“NoCache”选项。

有 3 个缓存选项:

“Cache”(默认)仅对在应用程序会话期间对视图的首次查询起作用,它会记住该查找结果供以后使用,直到您退出应用程序。“NoCache”绕过缓存直接指向视图。如果存在同一查找的缓存值,将不更新缓存。“ReCache”是一个容易忽略的选项,它通常直接指向视图,但它也使用查找值更新缓存。通过使用 ReCache,您可以在特定时间更新缓存,比如在保存查找所引用的文档时。在其他时候也可以使用缓存值,因为您知道对于用户输入的信息而言,这个值至少是最新的。

为查找选择正确的视图

有时对 @Db 函数最高效的视图并不是最好的。例如,@U nique(@DbColumn(“”:“NoCache”; “”:“”; “InvoicesByCompany”; 1)) 存在几个问题:

它在这里不应该使用 NoCache。您并不是每天都添加一个公司,即使添加,也可以在 Invoice 表单的 Postsave 中使用“ReCache”选项,让新添加的名字立即可用。当前的数据库是用表达式 “”:“” 指定的。相反,应该使用 “”,因为 “”:“” 不仅带有更多容易混淆的标点,而且它的计算速度也要慢一些。不要查找带有重复值的列表,然后再使用 @Unique 删除重复内容。相反,您应该查找其值具有惟一性的视图列,因为它们来自一个已分类的列。

最后一点特别重要,因为使用 100 个测试数据文档时能够很好工作的列查找,在实际使用中性能就会急剧下降,因此此时应用程序面对的是数千个文档。尤其是在服务器上使用该应用程序时,需要通过网络将视图列的完整内容发送给用户工作站,这是需要时间的。从视图读取已经存在的惟一值要快得多。

注意:在获取惟一值列表时,似乎可以使用“Generate unique keys in index”选项代替已分类的列,但实际上它存在一些缺点,因此不适合该用途。

查找需要花很长时间索引的视图也是不明智的,尤其是在选择公式或列公式中使用@Today 或 @Now 的视图。如果您仅需查找特定日期的文档,那么可以对仅包含这些文档的视图使用 @DbColumn,对包含所有按日期排序的文档的视图使用 @DbLookup,并提供日期作为查找键。

避免重复查找

不必要地使用 @Db 函数的方式有好几种。这里给出一些常见的方式:

在公式中重复

@If(@IsError(@DbLookup(“”: “NoCache”; “”; “SomeView”; CustID; 3); “”@DbLooku p(“”: “NoCache”; “”; “SomeView”; CustI D; 3))

这个公式不仅使用了不需要的 NoCache,并且进行了两次查找(实际上一次查找就可以了)。下面是两个修改后的公式:

_tmp := @DbLookup(“”; “”; “SomeView”; CustID; 3); @If(@IsError(_tmp); “”; _tmp)

@DbLookup(“”; “”; “SomeView”; CustID; 3; [FailSilent])

在读模式中使用不必要的关键字查找

当打开文档进行查看时,对于某些类型的关键字字段,Notes 客户端不需要知道选择列表。但复选框和单选按钮字段除外,在其中甚至以读模式显示所有选项,并且所有使用关键字的内容都是同义词(“Display text|value”),因为文档仅储存“value”,而表单必须显示“Display text”。

但是在其他情况中,需要编写该关键字公式来延迟查找,直到您实际需要选择列表为止:

_t := @If(@IsDocBeingEdited; @DbColumn(""; ""; "Customers"; 1); @Return(@Unavailable));@If(@IsError(_t); ""; _t)

通过在文档的读模式下返回 @Unavailable,公式会再次通知表单,让它询问随后是否需要选择列表。这将在用户切换到编辑模式并且指针点击该字段时发生。

因此,在用户仅查看文档时,您不仅要避免进行查找,并且要分散编辑文档时的延迟;8 个半秒延迟肯定没有一个 4 秒延迟那么烦人。如果用户的指针没有指向该字段,那么他们根本不需要进行查找。

在只需一个查找的位置使用多个查找

假设您将一个客户 ID 储存在“invoice”文档中,并想通过这个 ID 查找和显示客户的名称、地址和购买联系人姓名。这样,表单上就有几个 Computed for Display 字段,每个字段包含一个使用 @DbLookup(“”; “”; “CompanyByID”; CustID; x) 的公式,其中 x 是列号或字段名。

使用一个列来包含您需要的所有值会更高效,您可以从中找出每个字段值。因此这个列公式应该为:

CustName : StreetAddress : (City + “ ” + State + “ ” + Zip) : PurchasingContact

在您的表单上,添加一个隐藏的 Computed for Display 字段,名为 CustDetails,如下所示:

@DbLookup(“”; “”; “CompanyByID”; CustID; 4)

(假设合并的列为列 4)。然后,您就可以在需要显示名称的地方使用公式:

CustDetails[1] 等等。

在刷新时重复查找

假设您在组建表单时需要在计算字段中查找客户的经理的名字,如下所示:

@DbLookup(“”; “VOLE 1”: “EmpData. nsf”; “EmpByName”; @Name([CN]; @Username); “Manager”)

每次刷新表单时,都重新计算已计算的字段。许多表单需要经常刷新(因为您启用根据关键字字段的变化刷新字段的选项),因此这会严重影响速度。将字段设置为“Computed when Composed”会更好。

如果不需要将字段储存在文档中(记住,不要储存不需要存储的字段!),然后对它使用Computed for Display,但这个例子中,需要按照以下步骤避免在刷新时重复查找:

@If(@IsDocBeingLoaded;@DbLookup(“”; “VOLE 1”: “EmpData. nsf”; “EmpByName”; @Name([CN]; @Username); “Manager”);@ThisValue)

使用 @DbColumn 分配序列号

这是一个经常犯的错误。当设计人员必须为每个文档创建一个惟一的 ID 时,他们通常向最新的现有文档的编号加“1”。因此他们的公式如下所示:

tmp := @DbColumn(“”:“NoCache”; “”; “RequestsByNumber”; 1);     nextNumber := @If(tmp = “”;     1;     @ToNumber(@Subset(tmp; -1)) + 1);     @Right(“000000” + @Text(nextNumber); 7)

这是一个非常糟糕的主意。随着文档数量的增长,@DbColumn 的执行时间会越来越长。此外,当应用程序有多个用户时,它不能保证 ID 是惟一的,尤其是存在多个副本时。

如果在文档保存之后再给它分配序列号,那么序列号在此之前是不可用的,这很不方便。不过,如果在创建文档时就给它分配序列号,这将留有充足的时间让其他人使用相同的序列号创建并保存文档。

您可能需要重新考虑自己的需求。有时应用程序实际上仅需要惟一的非数字标识符,而我们却总是要求使用序列号。仔细查看 @Unique 函数,它生成一个很短但基本上是惟一的值(通过一些额外的工作就可以保证惟一性,例如为每位用户添加一个惟一的“前缀”,通常是他们的名称的首字母)。

如果您决定真的需要使用 序列号,那么请阅读 developerWorks 文章 Generating sequential numbers in replicated applications,它为使用序列号提供一种合理、高效的方法。一篇更多地讨论这个主题的 developerWorks 文章即将发布。

2012-03-07
浏览1843

回答者

擅长领域: portal中间件服务器

最近回答过的问题

回答状态

  • 发布时间:2012-03-07
  • 关注会员:1 人
  • 回答浏览:1843
  • X社区推广