Lotus開發性能優化 (II)

 

1.主要因素

一般而言,以下因素對應用程序的性能影響最大:

  • 視圖的數量及其複雜性。刪除不使用的視圖或合併相似的視圖。對於包含相同文檔但使用不同排序的視圖,使用一個可重新排序的列合併它們。刪除不需要的列,並簡化選擇和視圖列公式。檢查是否存在您不能訪問的“服務器私有”視圖或其他視圖。
  • 在視圖選擇公式或列選擇公式中使用 _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 也討論了類似的主題。
  • 用戶數量。如果服務器上存在大量用戶,那麼將會影響應用程序和服務器的性能。當應用程序的性能已經處於臨界狀態時,添加新的用戶會導致性能惡化。改良設計會有所幫助,但您還需要在其他服務器上創建副本(尤其是集羣服務器),或鼓勵用戶創建快的本地副本。

2.數據庫級別的性能因素

參考 Domino Designer 幫助文檔“Properties that improve database performance”的數據庫選項列表,您可以利用它調試性能。在很多情況下,這些選項通過削弱性能來獲得其他功能;因此,對於特定應用程序不需要的功能,可以禁用它。

對性能有巨大影響的選項包括:

  • Don't maintain unread marks。
  • Don't maintain the "Accessed (In this file)" document property。如果“不進行維護”,您就不知道最後一次讀取文檔的時間。對長時間不讀取的文檔進行歸檔時,這個信息非常有幫助。
  • Disable specialized response hierarchy information。如果禁用該項選,就不能使用 NotesDocument.Responses 屬性,也不能使用 @AllDescendants 或 @AllResponses,它們偶爾用於視圖選擇公式和複製公式。
  • Disable transaction logging。這個選項對性能的影響取決於管理員如何在服務器上設置它,以及用戶的數量。如果用戶很多,使用事務日誌能得到更佳的性能。嘗試啓用和禁用該選項造成的影響。事務日誌用於恢復。
  • Optimize Document Table Map。如果應用程序包含的各種類型的文檔大致相等,並且大多數視圖僅顯示一個類型(例如,SELECT Form = “xyz” & ...),這個選項就非常有用。如果視圖選擇公式都採用這種方式,並且先對錶單進行檢查,那麼視圖索引就會變快,因爲這能立即排除不使用該表單的文檔。

使用 NSFDB2(在 DB2® 數據庫中存儲 Domino 數據)並不能提升性能,事實上,使用傳統的 NSF 文件還會更快一些。NSFDB2 的目標是添加功能,而不是提升性能。

全文本索引可能會佔據大量的磁盤空間,但這通常是物有所值的。您可以利用全文本索引在代理中執行更快速的搜索,如果沒有這個索引,用戶必須使用更慢的搜索方法,這將導致長時間佔用服務器,從而造成性能下降。

注意:在 8.0 版本的 Notes 中,一個新數據庫屬性使您可以關閉非全文本索引數據庫的“全文本”搜索。一般而言,即使您擁有全文本索引,該選項也是很有用的;它確保索引被意外刪除之後,用戶會看到一條消息,而不僅是覺得性能無緣無故突然下降。

對於數據庫,除了屬性對話框之外,您還可以設置的另一個地方是 ACL 對話框。限制用戶創建視圖和文件夾能夠減少服務器的負載(見圖 1)。

圖 1. ACL 對話框
domino01

如果您取消選擇“Create personal folders/views”,用戶仍然能夠創建私有視圖,但創建的視圖必須儲存在本地的桌面文件中,而不是存儲在服務器上。因此不會對應用程序的性能造成太大的影響。

桌面私有視圖肯定會對性能造成影響,因爲索引它們時用戶必須從服務器實時提取數據。因此過多地使用桌面私有視圖還會使服務器陷入困境。所以要避免爲啓用“Private on first use”選項的用戶自動創建個人視圖。(下面會對此進行詳細闡述)。

3.公式性能

大部分 @ 函數都是相當快的,但有一小部分比較慢。因此要謹慎地使用它們:

  • @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 文章即將發佈。

4.表單設計

 

在這個小節中,我們將解決一下值得關注的問題。

能使用 Computed for Display 字段就不要使用 Computed 字段

因爲存儲字段一般都會使應用程序變慢,所以如果能夠在需要時輕鬆地計算這些值,就應該避免存儲值。這裏有一個折中;當文檔以讀模式打開時,不會計算 Computed 字段,因此如果它是一個很慢的公式,則最好儲存值,這樣能夠改善讀模式的性能(另一方面,這還意味着它會過期)。

但一定不要使用 Computed 字段重新顯示另一個字段的值 —— 這樣會存儲相同信息的兩個副本。

大量使用字段

在一個表單中使用大量字段的最常見原因是,一個表有多個行和列的信息,並且每個單元格有一個字段,超出了支持的行數。這是一種棘手的情況,因爲到目前爲止使用表單是最簡單的辦法。

不過,還有其他辦法可以管理表的值。最常見的辦法是將表放到一個富文本字段中,然後讓用戶根據需要進行填充(在富文本字段的默認公式中使用 @GetProfileField 從配置文件文檔讀取一個“starter”表)。這樣做的缺點是用戶在填寫表格時不能獲得幫助,要是存在私有字段的話,就可以提供關鍵字列表、轉換和驗證。不過,有時這也是一種可接受的辦法。

現在已經發布一些工具和技術,可以在對話框中每次僅編輯表的一行,然後在表中顯示結果。例如,Lotus Sandbox 中的 Domino Design Library Examples 包含一組設計元素,可用於在表中編輯和顯示數據,而不要求每個單元格必須有一個字段。在名爲“Table Editor”的數據庫文檔中,將詳細描述這個系統。需要付諸一定的努力才能實現它,但它對性能非常有幫助。

有時,我們在大部分文檔中可以看到包含許多空字段的表單。例如,大約 5% 的文檔需要一個包含 50 個字段的“Regulatory Approval”部分。而其餘 95% 的文檔則存儲了這些空字段,這不僅浪費空間,而且還造成糟糕的性能

對於這種情況,使用兩個不同的表單可能更好 —— 一個包含必需字段的主表單,和一個分開的“Regulatory Approval”表單,它可能是對原始文檔的響應,或者僅在需要時創建。在這裏,可以通過使用額外的文檔來避免使用更多的字段。

不要忘記多值字段。除了通過 5 個字段讓用戶輸入 5 個不同的值之外,還可以使用一個可以輸入多個值的字段。對條目的數量沒有任何限制(除非您選擇必須使用一個),並且生成的值在視圖和公式中更容易 使用。

注意:如果應用程序已經因爲字段過多而變慢,僅編輯設計元素是於事無補的;您必須編寫代理程序遍歷現有文檔,並從中刪除多餘的條目。已經出現一些業務合作產品可以簡化這個過程。不過,如果您的更改是一個重大的重組,比如將一些字段移動到特定的響應文檔中,那麼代理程序的編寫是相當複雜的。設計時從長遠考慮,爭取第一次就把事情做好,這能節省很多時間。

圖像過多

有些表單無節制地使用圖像,對背景使用大型位圖以及使用許多其他圖像修飾。大圖像需要更長的加載時間、佔用設計元素的緩存,並且查看錶單時需要更長的圖像呈現時間。創建表單時稍加註意就可以得到比較專業的外觀,並且不會對性能造成太大的影響。下面是一些技巧:

  • 不要將圖像複製粘貼到表單上;相反,要麼使用圖像資源設計元素,要麼導入圖像。如果您計劃在多個表單中使用同一個圖像,那麼可以使用圖像資源,因爲它允許客戶端將圖像與表單設計分離,然後再緩存它。即使您不打算 在多個表單中使用同一個圖像,將圖像作爲資源也是一個不錯的主意,因爲以後其他人可能需要使用該圖像創建另一個表單。
  • 將圖像放到表單上之後,不要隨意縮小它的尺寸。使用圖像編輯器(比如,GIMP)將原始圖像縮小爲您所需的尺寸 —— 即使您需要的是同一圖像的多個大小不同的圖像資源,也必須這麼做。

如果圖像的格式爲 JPEG,那麼可以嘗試不同的壓縮設置,看看能不能減小它的體積。JPEG 壓縮是“有損耗”的,因此壓縮後圖像可能會失真,但如果您在不影響視覺效果的情況下最大限度地壓縮圖像,可以加快表單的加載速度。您可以購買圖像工具,它們能幫助您找到一個平衡點。

  • 對圖像使用正確的文件格式。如果圖像使用有限的調色板(就像大部分徽標),GIF 格式的圖像文件可能是最小的。如果使用全色照片或繪畫,JPEG 可能是更好的選擇。不要使用 BMP 格式的文件,因爲它們的壓縮比很小。
  • 呈現表單元格和圖像單元格的背景需要一定的時間。隱藏單元格邊框的表單比顯示邊框的表單的呈現速度快,尤其是使用 3-D 效果的邊框。與嵌套在其他表內部的表相比,使用合併單元格的表的呈現速度更快。

存儲表單

不要使用存儲表單。

自動刷新字段

一般不要使用表單選項“Automatically refresh fields”。這個選項會在編輯表單時頻繁刷新它,重新計算已計算字段和輸入轉換公式,從而造成延遲。使用字段級別的選項“Refresh on keyword change”或字段事件 Onchange 或 Onblur 會更好,它們只在需要時進行刷新。

過多的共享設計元素

表單可以從其他設計元素獲取信息,比如圖像資源、共享字段、共享操作、子表單、摘要、樣式表和腳本庫。打開一個文檔可能會從除表單之外的許多其他設計元素讀取信息,讀取過程是需要時間的。共享設計元素的優點是使應用程序的維護更加容易,而它的不足之處是訪問多個元素需要更長的加載時間。

Lotus Notes 緩存設計信息,因此不需要每次都從原始設計元素讀取設計信息;然而,首次加載可能是個問題。緩存意味着使用共享設計元素可以提升性能,如果需要在許多不同的表單中使用相同的子表單或圖像的話。

共享操作不會損害性能,因爲僅有一個設計元素包含共享操作,所以多添加幾個共享操作與使用一個共享操作所需的開銷是一樣的。共享視圖列也不會影響性能。

由於共享設計元素有利於維護,所以除非採取各種措施仍然不能得到可以接受的性能,否則不推薦取消設計元素共享。

5.視圖

由於以下原因,低效和不必要的視圖會造成延遲:

  • 打開視圖時需要時間更新索引。
  • 當在 @Db 函數中使用視圖時,需要時間獲取信息。
  • 服務器上的更新任務會定期檢查每個視圖,看看是否需要使用最近修改的文檔更新它們。因此視圖越多(或越複雜),就越長時間地佔用服務器,導致所有應用程序變慢。

視圖打開緩慢的另一個常見原因是數據庫中存在大量文檔。當您打開視圖時,Lotus Notes 將檢查在最後一次視圖索引更新之後是否修改了某些文檔。您擁有的文檔越多,檢查所需的時間就越長,即使最終結果是“沒有最近修改的文檔”。

視圖中的 @Now 或 @Today

已經有許多文章介紹在不使用 @Today 或 @Now 時如何提供基於日期/時間的視圖。其中的一個例子就是 IBM Support Web 站點技術文檔 Time/Date views in Notes: What are the options?,它提供一些創建基於日期/時間的視圖的方法。

現在我們對其他幾個方面進行討論。首先,經常建議使用的@TextToTime(“Today”) 是不完整的。現在它僅適用於第一天。您必須修改它,讓它能夠正確地工作。

爲什麼?一般情況下,當您打開一個視圖時,Lotus Notes 就會查看“視圖索引” —— 視圖中存儲的文檔和行值的列表 —— 並僅檢查自索引最後一次更新之後創建或修改的文檔,以決定是否將它們添加到視圖,或刪除它們,或重新計算它們的列值。如果自從最後一次使用視圖之後沒有修改任何文檔,這個過程就會很快。

不過,如果您使用 @Today,舊的視圖索引就沒有用了。例如,假設選擇公式爲:

SELECT Status = “Processing” & DueDate <= @Today

可以將文檔添加到該視圖,即使這些文檔並沒有改變,因爲自從上一次使用視圖之後,@Today 的值就改變了。因此您每次使用這個視圖時,Lotus Notes 都會丟棄舊的視圖索引,並查看數據庫中的每個文檔,以決定這些文檔是否屬於該視圖,或重新計算列值。

如果您使用 @TextToTime(“Today”) 而不是 @Today,那麼您就可以“勝過”視圖索引器。祝賀您!Lotus Notes 將重用舊的視圖索引,並僅檢查被更改的文檔。這會得到更快的速度,但不幸的是得出的結果是錯誤的,因爲當 @Today 改變時,我們必須重新檢查所有文檔。 不必要的視圖

假設您有一個列,如果“請求”文檔在3個小時之後仍然打開的話,它將顯示一個紅色的感嘆號(使用 @Now 測試它)。這種情況會發生變化,即使在5秒鐘之前還在使用該視圖。然而使用 @Today 時,降低視圖索引的更新頻率可能更好。

您可以使用視圖屬性對話框中的視圖索引選項實現這個目的。在 Advanced options 選項卡上,您可以將視圖更新指定爲“Auto, at most every x hours”,其中 x 是您指定的值。這樣做的優點是大大加快了視圖的打開,但同時也存在缺點,那就是視圖不會立即顯示文檔更改,即便文檔已更改。用戶必須手動刷新視圖才能看到最新的數據。

另一個流行的代替辦法就是創建一個在夜間運行的調度代理程序,它通過更新視圖選擇公式(使用 NotesView.SelectionFormula 方法)來包含當天的選擇公式。例如,代理程序可能包含以下語句:

view.SelectionFormula = {SELECT Status=“Processing” & DueDate=[} & Today & {]} 

不過,它也有一些缺點:

  • 在所有副本顯示正確的文檔之前,必須在所有地方複製視圖設計更改。
  • 服務器管理員可能不信任更改產品應用程序的設計的代理程序。
  • 第二天早上打開視圖的第一位用戶仍然需要等待索引視圖。您可以將視圖索引選項設置爲“Automatic”,或讓代理程序刷新視圖,這樣就能夠避免這個問題。
  • 如果數據庫從模板獲取設計,您的視圖將被模板覆蓋。爲了避免這個問題,您可以讓代理程序在夜間設計刷新之後再運行,或將更改應用到模板。

另一個辦法就是在用戶界面上做出讓步。例如,您可以對視圖使用 “open requests by due date ”而不是“open requests that are overdue”,以在視圖的頂部對延遲的請求進行排序。它們很容易實現,實現之後視圖的打開會快得多。

在一些情況下,適合使用文件夾根據日期條件顯示一組文檔。在夜間運行的代理程序可以使用滿足該日期需求的文檔填充這個文件夾,並且文件夾上的訪問設置可以阻止用戶手動更改其內容。如果在白天編輯文檔時需要更改文件夾的內容,這就不是一個好選擇(雖然比較繁瑣,但您還可以使用定製代碼進行管理)。

 

許多應用程序很慢,因爲它們包含太多視圖,刪除一部分視圖將有所幫助。性能影響一般出現在服務器上,而不是特定的應用程序。

注意:數據庫的設計器不一定要訪問每個視圖。用戶的“Server private”視圖和其他帶有讀者列表的視圖是不可見的,但它們仍然會影響性能。服務器管理員可以在“Full access administration”模式下查看這些視圖。

默認的視圖刷新設置(Auto after first use, Discard index after 45 days)意味着超過 45 天后不使用的視圖索引將被丟棄,並且服務器不會再刷新它們。在這點上,它們對服務器性能的影響是很小的。然而,在摘要中使用視圖可能會導致用戶在搜索所需視圖時意外地使用它們。

因此,通過僅包含必要的視圖(用於完成用戶的特定任務,其命名方式便於用戶辨認),您不但可以改善性能,而且還提供更好的用戶體驗。

通常需要爲某個目的創建一次性使用的視圖,並且沒有程序記錄誰需要它們,誰使用它們,以及何時能夠安全地刪除它們。它們常常是“Server private”視圖,僅對它們的創建者是可見的,但它們仍然會影響性能。限制創建此類視圖能夠保持更好的性能(如果您想看看有哪些私有視圖,服務器管理員可以使用“Full access administration”模式列出它們)。

我們推薦對視圖使用 Comment 字段,用於描述視圖的任務、使用者和超過該日期就可以刪除視圖的“過期”日期(如果知道的話)。這樣,如果您對是否有必要使用視圖存在疑問,您至少知道應該問誰。如果您想刪除一個視圖,看看是否有人反對,可以將其複製粘貼到另一個未包含有文檔的數據庫,這樣您就可以在需要時找到它。

通常,應用程序包含的視圖僅是排列方式不同而已。這些視圖應該使用可重新排序的列合併到一個視圖中。儘管添加重新排序列的成本很高,但這要比擁有第二個視圖的成本低。

如果您在 Lotus Notes 8.0 中使用新的列選項“Defer index creation until first use”,那麼就更應該這樣做了。這個選項延遲爲重新排序創建索引,直到用戶請求它。這會給首位請求用戶造成很長的延遲,但如果沒有人請求的話,大家都能享受到更好的性能。

私有視圖

當您查找不需要的視圖時,要記住,開發人員不一定能夠查看應用程序中的所有視圖。如果用戶在服務器上存儲了私有視圖,或者共享視圖的訪問列表沒包含您的名字,那麼您就不能在設計器中查看這些視圖 —— 但它們仍然會影響性能。使用“Full access administration”模式的服務器管理員可以繞過訪問控制獲得所有視圖的列表(並刪除任意視圖)。

不必要的重新排序

因爲服務器必須通過額外工作才能在請求時執行交替視圖排序,所以除非有必要,否則不要啓用重新排序。升序和降序計數是兩種不同的重新排序,因此除非實際需要,否則不要同時啓用它們。在 Lotus Notes 8.0 中,如果您不確定是否使用了重新排序,那麼可以啓用該列上的“Defer index creation until first use”選項。

注意,您還可以選擇通過單擊列的頭部,將用戶導航到已由該列完成排序的不同視圖,這樣您就可以爲重新排序提供便利,並且省去額外的工作(如果另一個視圖已經存在)。

不必要的列

我們傾向於爲每個字段創建一個列,但應該避免這樣做。僅在視圖中包含用戶實際需要查看的信息即可;這樣屏幕就不會那麼擁擠,並且應用程序的速度會更快,佔用的存儲空間會更少。

過於複雜的公式

如果視圖列公式或選擇公式使用了循環函數(@For、@While和@Transform),或除註釋之外超過 200 個字符,那麼就需要簡化它。如果不能簡化它,那麼考慮將公式移動到表單的計算字段,讓視圖僅引用字段名。對於在多個視圖中使用的公式,這尤其有用。

即使您不使用計算字段,對於較長的公式,稍微思考一下就能簡化它。考慮使用 @Select 或 @Replace 代替較長的 @If 語句,並檢查邏輯,看看是否能夠通過不同的順序簡化測試。

要注意對列表的所有成員執行操作的運算符和 @Functions。沒有必要爲字符串列表上的許多簡單操作編寫循環;例如,要獲取每個元素的前三個字符,可以使用 @Left(listfieldname; 3) 來實現。

我們還有一些“組合操作符”,比如 *=,可用於比較來自兩個列表的組合元素,並且也能幫助您編寫更加緊湊的公式。

如果您使用其他語言編寫程序,您可能對邏輯運算符比較熟悉,它們僅對能夠決定連接值的表達式進行計算。例如,您可能希望得到這樣的表達式:

Form = “Report” & ( Sections = “Financials” | Total > 10000)

首先檢查 Form 是不是 Report,當這個條件爲真時,才測試表達式的其餘部分。在宏語言(和 LotusScript)中,邏輯運算符不是這樣工作的。表達式的兩部分都要計算。所以,如果計算第二部分的開銷比較大,您可以選擇如下所示的“懶惰邏輯”公式:

@If(Form = “Report”; Sections = “Financials” | Total > 10000; @False)

@If 函數的執行時間比 & 運算符長,但如果能夠通過它避免執行一些不必要的大開銷函數,您就領先一步了。

過度使用多個分類

分類是非常有用的。它們允許在同一視圖的多個標題下列出某個文檔。但不要過度使用它,因爲在一個視圖任務中兩次列出某個文檔所需的時間幾乎翻了一倍。如果每個文檔分別列出在 50 個類別中,再乘以文檔數就得出總行數,那麼服務器需要存儲和計算多少個行?這會給服務器帶來很大的壓力。

即使您不使用多個 分類,經過分類的視圖仍然比使用簡單排序的視圖慢。所需的時間取決於行數,而不是文檔數,並且每個文檔和類別標題都是一個行。

過度使用索引

視圖屬性對話框包含一組控制視圖索引的選項。這些選項很少用到,但選擇正確的索引選項能夠大大提升性能。

例如,假設數據庫包含特定的關鍵字文檔,您需要頻繁查找它們以填充表單上的關鍵字列表。關鍵字文檔是很少更改的,但應用程序中的其他文檔則需要經常更改。

在討論 @DbLookup 時我們已經知道對這種查找使用緩存是最好的,但第一次必須直接訪問視圖,因爲還不存在緩存值。當您執行這個過程時,Lotus Notes 發現在最後一次使用視圖之後文檔被更改了,然後將花時間查找被更改的文檔,並發現它們並不在視圖中。

對於在關鍵詞值 @DbLookup 中使用的視圖,不需要在每次使用時都進行重新索引。對於這種視圖,使用索引選項“Auto, at most every x hours”比較合適(見圖 2)。

domino02

如果沒有人使用這些視圖,服務器仍然會更新它們,但時間間隔要長些。偶爾可能會有不幸運的用戶刷新索引。但這會導致平均查找時間更短,並且同一個用戶在複雜表單的每個查找中都碰到刷新的機會不大,因此使用該選項後表單的打開速度會更快。

如果僅在每個季度的季度審覈時才使用視圖,那麼將索引保留 45 天沒有任何意義。將其設置爲在 2 天之後丟棄,這樣能夠減輕服務器的工作。

在其他情況下,選擇適當的索引選項能夠改善性能。想辦法確定您的視圖應該使用什麼設置是值得的。

注意:可以通過程序在當前的副本中刷新索引,比如使用 NotesView.Refresh 方法。假設一個索引在正常情況下很少更新,但當您保存某個向視圖提供數據的表單時,則必須更新視圖,以在查找中使用新的數據。在表單的 Postsave 代碼中,對視圖使用 Refresh 方法。同時,您可以使用帶有 ReCache 的 @Db 函數,將特定查找的緩存更新到視圖。

Reader 字段

當您需要 Reader 字段時,沒有什麼東西可以代替它,但它可能會大大損害視圖性能。當您打開包含帶有 Reader 字段的文檔時,Lotus Notes 會對行進行掃描,查找您可以訪問的行。當行的數量填滿屏幕時,將停止查找。如果您僅能訪問一個文檔,它必須查看視圖中的每個行進行確認,這可能需要花很長時間。

對此,您可以:

  • 使用比較短的 Reader 字段值。在單一角色中檢查成員關係要比根據一個長長的訪問名稱列表比較它們快(使用角色還便於維護)。
  • 避免在這種應用程序中使用視圖。如果用戶僅能訪問一兩個文檔,您可以提供其他訪問方式,例如,向他們發送包含有這些文檔鏈接的電子郵件。
  • 使用嵌入式單類別視圖,這種視圖僅顯示包含“它們的”文檔的類別。
  • 使用設置爲顯示空類別(即未包含文檔的類別)的分類視圖。當然,這樣做使得用戶查找文檔更加困難,除非您爲用戶提供導航,因此您應該將該功能和 @SetViewInfo 結合使用,以僅顯示用戶所需的類別。

注意:使用分類視圖存在安全問題;即您在文檔中向用戶顯示了一個字段(類別),他們本來是不可以訪問該字段的。要確保使用這種辦法是可行的!

  • 鼓勵用戶使用本地副本。因爲本地副本僅包含用戶能夠訪問的文檔,因此不需要花功夫隔離他們不能訪問的文檔。

不要單獨使用 Reader 字段作爲導航幫助;例如,這是一種幫助用戶方便地查找所需文檔的方法,因爲它們是用戶能夠在視圖中看到的所有文檔。如果文檔中的信息不是隱私的,還有其他更好的方法可以幫助用戶找到所需的文檔,如前一小節和下一小節所述。

Private on first use

在共享視圖的選擇或列公式中使用 @UserName 和 @UserRoles 時,不能得到滿意的結果。這是開發人員創建僅顯示“My Documents”的“Private on first use”視圖的常見原因。這些視圖可能存儲在服務器上(將從總體上影響應用程序的性能),或者存儲在用戶的本地“桌面”文件中。

桌面視圖不會直接影響服務器的性能,但當打開其中一個視圖時,將像其他視圖一樣進行重新索引,以顯示最近的更改。這意味着用戶工作站必須向服務器請求自從最後一次使用之後修改的所有文檔。這個過程可能造成用戶等待,如果許多用戶執行該操作,服務器還可能會因爲大量發送數據請求而陷入困境。

注意,視圖索引僅使用摘要 數據,因此大型的富文本字段和文件附件在這裏不構成問題。

除了性能問題之外,私有視圖還面臨維護方面的問題,因爲開發人員沒有簡單的方法更新用戶私有副本的設計。在這種情況下,共享列也不起作用,因爲要在視圖中更新共享列,執行更新的人員必須能夠訪問該視圖。

通常可以使用 Notes 視圖的“single category”功能避免“Private on first use”。例如,如果您正在顯示“My Documents”,您可以使用根據所有者分類的視圖,然後要麼使用“single category”公式將該視圖嵌入到表單或頁面中,要麼在視圖中的 Postopen 事件中使用 @SetViewInfo,僅對當前用戶進行顯示。因爲只有一個共享視圖,所以總體索引開銷降至最低,並且私有用戶不必像在桌面私有視圖中那樣等待,因爲索引幾乎總是最新的。

6.代碼

當您開始編寫 LotusScript 或 JavaTM 代碼時,您可能就開始逐步損害性能。在這裏,我們討論一些常見的問題。

GetNthDocument

使用 NotesDocumentCollection.GetNthDocument 遍歷集合是非常慢的;應該改用GetFirstDocument 和 GetNextDocument。對於某些類型的集合使用 GetNthDocument 也一樣高效,但不使用它事情更好辦。

表單或視圖包含的操作代碼過多

如果表單、視圖或文件夾有許多操作,您就需要在設計元素中爲每個操作編寫代碼(使用共享操作也是如此),這樣就存儲了許多代碼,每次使用設計元素時都必須將它們加載到內存中。

在大部分時間,僅用到許多操作中的其中一兩個,因此您需要等待加載所有操作。如果操作出現在多個地方,您就在設計緩存中多次緩存了相同的代碼,從而佔用應該用於其他用途的內存。

可以考慮將一些操作移動到代理程序中。這樣,當有人請求運行操作時,僅在內存中加載一個代碼副本。可以用宏語言編寫操作按鈕,以使用 @Command([RunAgent]) 調用代理程序,這能大大減少隨設計元素一起加載的代碼。

如果您允許用戶創建私有視圖或文件夾時,這尤爲重要,因爲他們的文件夾將多次複製操作代碼,這不僅佔用空間,而且還不能進行更新,除非用戶手動刪除私有視圖。

腳本庫過多

加載多個腳本庫所需的時間並不是線性增長的。即加載 10 個腳本庫所需的時間比加載 5 個腳本庫所需的時間的 2 倍還要多,腳本庫使用了其他庫時尤其如此。

這在未來可能會得到改變,儘管如此,也存在一個平衡點;訪問兩個設計元素比訪問一個包含相同數量代碼的設計元素所需的時間長。將經常一起使用的腳本庫合併起來能夠節省加載時間,儘管有時會加入不需要在特定代理程序中調用的代碼。

ComputeWithForm

NotesDocument 的 ComputeWithForm 方法是在文檔中更新計算字段但不復制代碼的便捷方法。不幸的是,這比“手動”計算和分配新字段值更慢。如果您的代理程序很慢並且使用了 ComputeWithForm,向 ComputeWithForm 添加幾行用於爲特定字段賦值的代碼就能夠大大加快程序的速度。

自動更新視圖

默認情況下,使用 NotesView 對象時,它將爲視圖實現常規的索引刷新屬性。例如,假設您更新一組“Vegetable”文檔,作爲這個過程的一部分,您必須在同一數據庫下的“Pests”視圖中查找破壞該植物的害蟲。但是當您保存了一個 Vegetable 文檔時,另一個文檔又被更改了。

當您處理下一個文檔,並查找“Pests”視圖時,Lotus Notes 就知道視圖索引已經過期,然後刷新它。您所做的更改不會影響 Pests 視圖,但在檢查已更改的文檔之前,Lotus Notes 並不知道這點。

對於這個例子,使用 NotesView 的 AutoUpdate 屬性告訴 Lotus Notes 不必更新視圖索引是個不錯的主意,除非您使用 Refresh 方法顯式地請求它。這能夠大大提升速度。

即使您所做的更改影響到 NotesView 的內容,也可以使用這種方法,只要您的更改對自己正在做的事情沒有影響即可。例如,您知道更新將從視圖刪除文檔,但這沒有關係,因爲您已開始處理下一個文檔。

不能使用基於高效集合的方法

NotesDocumentCollection 類有一些以“All”結尾的方法,它們能夠處理集合中的所有文檔。您應該瞭解這些方法,因爲它們比遍歷集合和操作每個文檔快得多。(除非您需要對每個文檔執行多個操作;否則遍歷會更快,每個文檔僅需保存一次)。

重複開銷大的操作

內置類中的某些方法和屬性非常慢。如果不需要,就避免使用這些函數,這能讓代碼的運行更快。例如,假設您處理一個文檔集合,必須使用每個文檔的一個字段作爲查找值,以從其他視圖獲取信息:

Dim view As NotesView
Set docCur = coll.GetFirstDocument
Do Until docCur Is Nothing
Set view = db.GetView(“CustomersByID”) 'oops! Don't do this in the loop! 
Set docCust = view.GetDocumentByKey(docCur. CustI D(0), True)
...
Set docCur = coll.GetNextDocu ment(docCur)
Loop

在這段樣例代碼中,如果 coll 包含 1000 個文檔,我們將調用開銷很大的 GetView 方法
1000 次。如果調換 Do Until Set view 代碼行的位置,代碼的運行就會快得多,因爲
GetView 僅被調用一次。

使用代理程序探查器查找這類東西是個不錯的方法。developerWorks Lotus 文章 Troubleshooting application performance: Part 1: Troubleshooting techniques and code tips 和 Troubleshooting application performance: Part 2: New tools in Lotus Notes/Domino 7 將對此進行描述。

保存未更改的文檔

還記得嗎,影響性能的因素之一就是修改文檔的頻率。當您編寫處理文檔的代理程序時,應該避免不必要地保存文檔更改。在爲每個項賦值時,檢查它是否已經擁有該值。如果最終沒有修改任何東西,就不要調用 Save 方法。通常,您可以使用搜索方法從文檔集合中過濾出不需要處理的文檔。

如果您總是檢查各個項以確定是否更改它們,代理程序的運行可能慢些。但也可能不變慢,因爲保存文檔比在內存中比較信息需要更多時間。如果總是執行檢查,應用程序的其他部分會更高效,比如複製、視圖索引和全文本索引。

避免不必要的保存也可以減少複製衝突。複製使用 修改時間;它並不將整個文檔發送給其他副本,而是僅發送修改的項。所以,即使最終需要保存文檔,如果只修改需要新值的項,那麼就能減少複製時間。使用本地副本的用戶將受益匪淺。

搜索文檔的方法

大部分代理程序必須做的一件事情是查找一組需要處理的文檔。有很多查找文檔的方法,但不同的方法適用於不同的情況。

developerWorks Lotus 文章 Lotus Notes/Domino 7 application performance: Part 1: Database properties and document collections 討論搜索和處理文檔集的不同方法。下面概括地介紹一下:

  • 如果視圖包含您需要的文檔,並且以有用的方式進行排序,那麼從視圖讀取文檔就會更快,例如使用 GetAllDocumentsByKey 方法。
  • 對於包含大量文檔的數據庫,FTSearch 方法比 NotesDatabase.Search 方法要快,前提是數據庫必須是全文本索引的。注意,您也可以通過在代理程序的 Document Selection 事件中輸入全文本搜索來執行它。

在這兩種情況中,利用服務器事先完成的索引工作可以節省時間。與必須檢查每個文檔的 NotesDatabase.Search 相比,FTSearch 節省了一些運行時工作。

全文本搜索的文檔過濾級別不如 NotesDatabase.Search 細,但它節省了大量時間,您完全可以利用一部分時間遍歷結果並忽略不適用的部分。在 Notes Client 幫助(不是 Designer 幫助)中的文章“Refining a search query using operators”介紹了完整的全文本查詢語法。您將發現它的用途比您想象的要多。

注意:取決於自己的需求,有時您可以結合公式搜索的威力和全文本搜索的性能,在使用基於公式選擇條件的視圖中進行全文本搜索。

從緩存中刪除不使用的文檔

在早期的 Lotus Notes 版本中,使用完 NotesDocument 對象之後,可以使用 Delete語句從內存中刪除它們。然後,從 6.0 版本開始,再也不需要這樣做了。(如果您已經知道是怎麼回事,就不要再使用它。如果不知道,也不用擔心)。

出於其他非性能方面的原因,您可能還在使用 Delete,例如,您認爲其他進程修改了文檔,因爲您最終打開它,並確保自己看到的是最新數據。

更高效的循環和賦值等

有一些文章比較了“for”循環和“while”循環、全局變量和堆棧變量等的性能。然而,除非您的應用程序屬於計算密集型的,否則難以利用這些比較獲得性能改善。

大部分腳本花在打開文檔和視圖的時間遠比處理變量值多。避免不必要的數組引用可以節省百萬分之一秒;然而,打開不必要的視圖可能要花費數秒鐘。如果想通過節省時間來提高性能,那麼應該先考慮佔用時間多的項。

瞭解不同 LotusScript 表達式和語句的性能特點可能非常有用,但養成良好的代碼編寫習慣更有用;回過頭來修改不妥當的地方常常是得不償失的。

關於這個方面的有價值技巧是:

  • 不要使用 GetNthDocument(如前所述)
  • 顯式地聲明變量,避免使用默認的變量類型。這樣做不僅能夠提供更好的性能,並且有助於在編譯期間查找錯誤。對於自動將 Option Declare 語句插入到您的 LotusScript 代碼中的編程面板屬性,應該使用這個選項。

7.使用 LC LSX

如果將 LC LSX 與外部關係數據庫或數據文件整合起來,它通常比內置的 ODBC 類快。
IBM Redbooks Implementing IBM Lotus Enterprise Integrator 6 包含了大量信息,討論如何使用這個 API 進行編程,以及如何最大限度地提升性能。

8.測試

這份白皮書在開始時已經提到,很多優秀的小程序在數據和用戶的數量比較少時表現非常出色,但當用戶和數據非常多時,它們就陷入困境了。使用大量文檔測試設計是明智的。測試 50 人同時使用應用程序時有什麼反應(您可能很難找到 50 位朋友抽時間參與測試,但可以使用能夠模擬該場景的自動化測試工具)。

此外,編寫包含少量樣例數據的代理程序也不是很難,然後通過爲選擇字段分配隨機值增加數據的數量,使文檔多達數千個。如果您使用代理選項創建新文檔的話(在代理程序編輯屏幕的右下角),這些工作還可以通過公式代理程序完成。

警告:如果您測試已投入生產的應用程序,務必在非生產服務器的數據庫拷貝(不是 副本)上進行測試,並且最好使用不用於複製生產服務器的服務器。這樣,就不用擔心破壞生產服務器上的數據,或阻礙使用該服務器的人員的工作。

9.使用配置文件文檔

配置文件文檔是高效地儲存和獲取不經常改變的信息的好辦法。因爲文檔在首次使用時就被完整地緩存起來,所以使用它保存定製關鍵字列表是非常高效的。這不會影響視圖索引,也不用擔心控制緩存。它們的複製和普通文檔是一樣的(但使用複製選擇公式的用戶不會意外地破壞應用程序,這比普通的關鍵字文檔要好)。它們既有趣又簡單,您可以嘗試使用!

發佈了5 篇原創文章 · 獲贊 2 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章