許多應用程序專為使用多個層而設計。在這樣的應用程序中,就應用程序的 整體響應而言,對數據訪問層的調用的性能至關重要。使用多個層可以提高應 用程序的靈活性。n 層方法也可以幫助實現關鍵組件的隔離,這可用於提高可 靠性和可伸縮性。將組件隔離到不同層中後,可以更輕松地在可用計算資源間 分布,因此使用多個層可以提高可伸縮性。
層交互分析 (TIP) 旨在幫助您了解應用程序所依賴的數據層的性能。TIP 是 Visual Studio 分析工具提供的一種新功能,用於測量和報告 .NET Framework 應用程序在等待對 ADO.NET 兼容數據庫的同步調用完成時,所經歷 的數據層延遲持續時間。對於經常調用數據庫而又注重響應時間的應用程序, TIP 可以幫助您了解哪些數據請求是造成響應延遲的主要原因。
本文將介紹 TIP 並演示其報告功能。另外還將討論 TIP 所依賴的檢測技術 ,並提供一些有效使用 TIP 診斷與數據庫活動相關的性能問題的最佳實踐。本 文將逐步介紹在以下示例環境中使用 TIP:數據密集型的雙層 ASP.NET Web 應 用程序,並且使用 LINQ to SQL 技術從 Microsoft SQL Server 訪問數據。最 後將討論如何使用標准 SQL 管理員性能工具來增加 TIP 性能數據,以便加深對 數據層性能的理解。
TIP 入門
TIP 會動態添加檢測代碼,用於在分析運行期間測量應用程序的數據層調用 的持續時間。Visual Studio 分析工具在 Visual Studio 2010 Premium Edition 和 Visual Studio 2010 Ultimate Edition 中提供。
若要啟動分析會話,可以從“Analyze”(分析)菜單單擊“Launch Performance Wizard”(啟動性能向導),也可以從“Debug”(調試)菜單單 擊“Start Performance Analysis”(啟動性能分析),或者使用 Alt+F2 快捷 鍵。在性能向導的第一頁上,將要求您選擇一種分析方法。
TIP 適用於任何一種分析方法(采樣、檢測、內存或並發),但默認情況下 未啟用。若要啟用 TIP,您需要在性能向導的第三頁上取消選中“在向導完成 後啟動分析”復選框。(由於 TIP 尚未啟用,您現在無法啟動應用程序並開始 分析。)為了得到最佳結果,建議在一開始選擇采樣分析方法,這尤其適用於您 最注重的是數據層交互數據的情況。這主要是因為采樣對應用程序性能的影響 最小。
若要啟用 TIP 數據收集,請在性能向導中訪問剛剛在“Performance Explorer”(性能資源管理器)窗口中創建的性能會話,然後右鍵單擊以查看其 屬性。在屬性對話框中,選擇“Tier Interactions”(層交互)選項卡,然後 選中“Enable tier interaction profiling”(啟用層交互分析)復選框。單 擊“OK”(確定)按鈕可關閉該對話框。此時 TIP 已經啟用,您已准備好開始 對應用程序進行分析。
若要開始運行實際分析,請單擊“Performance Explorer”(性能資源管理 器)窗口中的“Launch with Profiling”(啟動並分析)工具欄按鈕。
有關在 Visual Studio 中啟用 TIP 的完整說明,請參見 Habib Heydarian 的博客文章“Walkthrough:Using the Tier Interaction Profiler in Visual Studio Team System 2010”,地址為 http://blogs.msdn.com/b/habibh/archive/2009/06/30/walkthrough-using- the-tier-interaction-profiler-in-visual-studio-team-system-2010.aspx。
TIP 測量性能的方法
TIP 在分析運行期間將代碼插入應用程序,用於測量對應用程序所使用的 ADO.NET 數據層的調用。當層交互分析活動時,Visual Studio 分析器檢查解 決方案中的 Microsoft 中間語言 (MSIL),查找對 ADO.NET 函數的引用。調用 實時 (JIT) 編譯器以生成由應用程序運行的本機代碼之前,分析器插入向關鍵 ADO.NET 方法添加檢測代碼的指令。此檢測代碼跟蹤每次 ADO.NET 調用期間所 用的時間量。
隨著應用程序執行,TIP 將捕獲和記錄計時數據。對應用程序進行分析時, 此檢測代碼將記錄執行的所有 ADO.NET 調用的持續時間,同時還捕獲數據庫調 用中使用的命令文本的副本。在應用程序運行期間,TIP 會收集使用 ADO.NET 類同步方法(包括 SQL、OLE DB、開放數據庫連接 (ODBC) 和 SQL Server Compact (SQL CE) API)執行的所有數據庫訪問的計時數據。如果應用程序使 用 LINQ to SQL 或實體框架 API 訪問 SQL 數據庫,則 TIP 還將捕獲計時數據 。
計時數據與分析會話期間收集的所有其他數據一起存儲到 Visual Studio 分 析器文件 (.vsp) 中。由於調用外部數據庫的應用程序執行的是進程外調用, 因此 TIP 添加用於檢測應用程序的指令對應用程序整體性能產生的影響非常小 。
性能優化
在常見設計模式中,將 Web 應用程序劃分為表示層、業務邏輯層和數據層。此設計模式會導致將應用程序劃分為各個組件,以實現可靠性、可擴展性和可伸 縮性。多層應用程序使用業務邏輯組件訪問其數據層。這些組件將數據實體在 邏輯上組織為一組相關表中的行和列,以此引用數據實體。根據設計,SQL Server 等數據庫維護與數據庫表關聯的物理數據的方式對應用程序是透明的。
出於可靠性和可伸縮性考慮,大型 Web 應用程序通常在池中配置多台計算機 ,負責與應用程序的各層關聯的邏輯處理。使用多台計算機支持多個層在性能 分析方面造成了特殊的挑戰,這是因為監視任意一台計算機都只能提供不完整的 應用程序信息。
例如,使用 SQL Server 之類的數據庫系統管理和仲裁對應用程序數據存儲 的訪問,會造成數據層與應用程序邏輯的隔離。駐留在諸如 SQL Server 之類 的數據庫中的應用程序數據在獨立的進程地址空間中維護,具有自己的數據存儲 。包含應用程序數據的數據庫可以與應用程序駐留在同一台物理計算機上,但 更傾向於使用網絡協議從不同計算機進行訪問。
應用程序傳遞到外部數據庫的 SQL 命令以及在數據庫上操作的命令為進程外 調用。采樣分析在等待這些進程外調用完成時,將應用程序視為休眠,但無法 確定應用程序等待的原因或者這些延遲的持續時間。檢測和並發分析會測量這 些延遲的持續時間,但無法確定所發布的 SQL 命令以及這些命令花費如此長的 時間來完成的原因。
在與外部數據庫通信的多層應用程序中,數據庫組件通常在整體應用程序響 應時間中占據主要比例。Visual Studio 分析工具包括多種分析方法,其中有 采樣、檢測、內存分配、和並發,但如果沒有 TIP 數據,這些方法對於確定與 訪問外部數據庫相關的性能問題都沒有多少幫助。
使用 TIP 數據可以深入剖析與數據庫相關的延遲,並了解發生延遲的原因。將此數據與數據庫供應商提供的其他性能工具一起使用時,您還可以了解采取哪 些操作來提高很大程度上依賴於數據庫性能的應用程序的性能。
由於 TIP 將檢測代碼添加到應用程序代碼中,因此可以收集與數據庫命令相 關的計時數據而不受所訪問數據庫實例的物理位置的影響。例如,對於與應用 程序駐留在相同物理計算機上的 SQL Server 實例(這是單元測試中的一種常見 情況),可以收集該實例的進程外調用的計時數據。當同一個應用程序准備好 針對其他物理計算機上的 SQL Server 實例執行集成或負載測試時,TIP 可以繼 續收集該配置的測量數據。實際上,使用 TIP 測量可以比較這兩個不同配置的 性能。
您可以通過 TIP 比較和對照多個外部數據庫性能和可用優化選項的影響,包 括高速緩存配置、物理數據存儲設備、數據庫分區、數據庫索引和數據庫表設計 。此外,您可以直接測量在虛擬機上運行 SQL Server 的性能影響。
TIP 報告基本信息
當激活了 TIP 的分析會話完成後,將在“層交互”視圖中匯總應用程序與其 ADO.NET 數據層的任意交互的相關計時數據。圖 1 顯示激活了 TIP 數據收集 並且在分析運行期間存在 ADO.NET 活動的分析器示例。
圖 1 Visual Studio 分析器層交互報告
報告的上半部分是收集的分析數據的摘要。對於 ASP.NET 應用程序,該視 圖按 URL 組織。報告按照 URL 對 Web 應用程序 GET 請求的服務器端響應時 間分組。
在應用程序層下,報告顯示與數據庫層(在本例中為 AdventureWorks 示例 數據庫)的各個連接。其中測量和報告的是 ASP.NET 請求的服務器端處理時間 部分,這一部分與使用 ADO.NET 的同步數據庫調用相關。在本例中顯示了三行 摘要,每一行匯總了與所分析網站中三個不同 ASP.NET 頁面相關的數據庫活動 。對於在分析期間標識的每個 ASP.NET 頁面,將報告分析運行期間處理的 ASP.NET 請求數以及生成的每個響應消息的服務器端響應時間。
額外的摘要行顯示其他 GET 請求的響應時間數據,包括對樣式表、 Javascript 代碼和頁面中鏈接的圖像的請求。分析器無法與特定 ASP.NET 請 求關聯的任何數據庫調用都分組在“Other Requests”(其他請求)類別下。
在分析使用數據層的 Windows 桌面或控制台應用程序時,該報告將按進程名 稱對 ADO.NET 活動進行劃分。
在每個網頁摘要行下列出單獨一行摘要,其中報告在 ASP.NET 處理期間發出 的同步數據庫請求數,這些請求按照數據庫連接進行組織。在本例中,您可以 看到在單個數據庫連接中處理了六個對 CustomerQuery.aspx 的 ASP.NET 請求 。這六個請求在服務器上的總處理用時為 0.959 秒,平均響應時間為 160 毫 秒。這些請求發布了 12 個 SQL 查詢,用時約 45 毫秒完成。與為此網頁生 成響應消息相關的用時中,對數據庫請求的等待時間僅占約 5%。
如果突出顯示其中一個數據庫連接摘要行,則“層交互”視圖的下半部分將 詳細列出應用程序發出的特定 SQL 命令。SQL 命令按照發布的命令文本分組, 並按照在該頁面組中的用時排序。
在本示例中,一條 SQL 命令發布了三次,另一條命令發布了六次,第三個查 詢發布了三次。對於詳細視圖,在摘要報告中累計到單行中的各個特定查詢的 用時將分別報告。您可以查看總用時、該命令在所有實例上的平均用時以及對 於每個查詢所觀察到的最短和最長延遲。
如果雙擊 SQL 命令詳細信息行,則將在“Database Command Text”(數據 庫命令文本)窗口中顯示所發布的 SQL 命令的完整文本。這是應用程序在執行 期間通過 ADO.NET 接口傳遞到數據庫的實際命令。如果請求針對的是存儲過程 的執行,則將顯示對存儲過程的特定調用。
LINQ to SQL 示例
現在介紹一個使用 TIP 的簡單示例,通過它可以了解很大程度上依賴於從數 據庫訪問信息的 ASP.NET 應用程序。
對於使用 LINQ to SQL 訪問存儲在外部 SQL Server 數據庫中的數據的應用 程序,TIP 尤為有用,這是因為 LINQ 的目的是讓開發人員無需深入了解物理數 據庫及其性能特征。使用 LINQ to SQL,在對象關系設計器中創建的“實體:關 系”(E:R) 圖表會生成隨後由 Visual Studio 用作模板的類,用於自動構建語 法正確的 SQL 命令。
由於使用 LINQ to SQL 時無需考慮大部分 SQL 語言編碼方面的注意事項, 因此 LINQ to SQL 也常常會掩蓋與數據庫設計、配置和優化相關的重要性能注 意事項。如本例所述,使用 LINQ,您可以方便地創建聯接多個表的復雜查詢, 而無需考慮這樣做的性能影響。
使用 TIP,您可以查看 LINQ to SQL 生成的實際 SQL 命令文本,並收集這 些 SQL 查詢的運行時執行的度量。然後,您可以使用手頭的 SQL 命令文本訪 問其他數據庫優化工具,幫助您更好地了解任意特定 LINQ to SQL 操作對性能 的影響。
本文中的示例是一個 Web 窗體應用程序,該應用程序使用特定客戶 ID 查詢 AdventureWorks Sales.Customer 表來檢索該客戶的訂單歷史記錄。此查詢中 涉及的 AdventureWorks 表包括 Customer、SalesOrderHeader、 SalesOrderDetail 和 StateProvince 表,如圖 2 中的對象關系設計器視圖所 示。
圖 2 查詢 Sales.Customer 信息所用的 AdventureWorks 表
如果您希望隨訂單歷史記錄顯示客戶的郵寄地址和電子郵件地址信息,則需 要訪問 CustomerAddress、Address 和 Contact 表。如對象關系設計器中所示 ,AdventureWorks 表包含 CustomerID、SalesOrder 和 ContactID 等主鍵和外 鍵,使得這些表可以按照邏輯方式聯接起來。
圖 3 中顯示了使用 LINQ to SQL 創建 AdventureWorks 客戶查詢的 C# 代 碼。在本例中,custid 是請求的特定 CustomerID 值。此查詢返回一個 customeryquery 集合,其中包含單獨的一行數據,提供在 select new 子句中 列出的數據字段。
圖 3 LINQ to SQL 客戶查詢
var customerquery =
from customers in db.Customers
from custaddrs in db.CustomerAddresses
from addrs in db.Addresses
where (customers.CustomerID == custid &&
customers.CustomerID == custaddrs.CustomerID &&
custaddrs.AddressID == addrs.AddressID)
select new {
customers.CustomerID,
customers.CustomerType,
addrs.AddressLine1,
addrs.AddressLine2,
addrs.City,
addrs.StateProvince,
addrs.PostalCode,
customers.TerritoryID
};
然後,可以將 customeryquery 綁定到 ASP.NET 網頁上的控件:
DetailsView1.DataSource = customerquery;
DetailsView1.DataBind();
現在,可以創建查詢以檢索此客戶的訂單歷史記錄:
var orderquery =
from orderhdr in db.SalesOrderHeaders
where (orderhdr.CustomerID == custid)
orderby orderhdr.OrderDate
select new {
Date = orderhdr.OrderDate.ToShortDateString(),
orderhdr.AccountNumber,
InvoiceNo = orderhdr.SalesOrderID,
orderhdr.TotalDue
};
執行此 LINQ to SQL 操作時,orderquery 將包含與特定客戶 ID 關聯的 OrderHdr 表中每一行相對應的行。如果客戶歷史記錄指示有多個銷售交易,則 orderquery 集合將包含多行。
這些查詢表面上非常簡明。但是,使用 TIP 便可了解這些看上去簡單的 LINQ to SQL 操作的性能影響。
使用 TIP 數據進行優化
現在,讓我們進一步了解 customerquery。在運行時,LINQ to SQL 使用 LINQ 語句中隱含的邏輯數據庫 SELECT 操作,並使用它生成聯接以下四個 AdventureWorks 表中的數據的有效 SQL 命令:Customers、CustomerAddresses 、Addresses 和靜態 StateProvince 表。在此處的 LINQ to SQL 代碼中看不 到這一點。
在 Visual Studio 分析器下運行此代碼時,TIP 檢測會報告此查詢執行的次 數,並測量網頁等待執行的延遲時間。實際上,這是在分析運行期間執行了六 次的操作,如圖 1 中所示。
此外,如上文所述,LINQ to SQL 代碼生成的 SQL 命令在分析應用程序時也 可用。圖 4 顯示了此操作的實際 SQL 命令。
圖 4 customerquery 的 SQL 命令
SELECT [t0].[CustomerID], [t0].[CustomerType], [t2]. [AddressLine1], [t2].[AddressLine2], [t2].[City], [t3]. [StateProvinceID], [t3].[StateProvinceCode], [t3]. [CountryRegionCode], [t3].[IsOnlyStateProvinceFlag], [t3].[Name], [t3].[TerritoryID], [t3].[rowguid], [t3].[ModifiedDate], [t2]. [PostalCode], [t0].[TerritoryID] AS [TerritoryID2]
FROM [Sales].[Customer] AS [t0]
CROSS JOIN [Sales].[CustomerAddress] AS [t1]
CROSS JOIN [Person].[Address] AS [t2]
INNER JOIN [Person].[StateProvince] AS [t3] ON [t3]. [StateProvinceID] = [t2].[StateProvinceID]
WHERE ([t0].[CustomerID] = @p0) AND ([t0].[CustomerID] = [t1].[CustomerID]) AND ([t1].[AddressID] = [t2].[AddressID])
請注意,SQL 命令文本中包括一個令牌(在此處指定為“@p0”),用於表示 LINQ 提供給查詢中的客戶 ID 參數。
現在,已經得到了由 LINQ 生成的實際 SQL 命令文本,可以了解數據庫設計 如何影響查詢的性能。
此時可以執行的操作是在 SQL Server Management Studio 中執行此 SQL 命 令,並檢查其執行計劃,如圖 5 中所示。若要訪問此查詢的執行計劃,需要添 加命令以指向合適的數據庫:
USE AdventureWorks ;
GO
接下來,從 TIP 報告中復制 SQL 命令文本,請記住使用數據庫中的有效 CustomerID 替換“@p0”令牌。然後,在 SQL Server Management Studio 中 執行此示例查詢並訪問執行計劃,該執行計劃顯示查詢優化器如何將邏輯請求轉 換為物理執行計劃。
圖 5 示例 LINQ to SQL 操作的執行計劃
在本例中,查詢的執行計劃顯示 SELECT 語句使用 CustomerID 字段上的聚 集索引訪問 Customer 表,該語句返回並且只返回表中的一行。在 SQL Server Management Studio 中,您可以將鼠標懸停在某個操作上以查看其屬性,或者突 出顯示該操作並右鍵單擊以查看“Properties”(屬性)窗口。使用這種方式 ,您可以循環逐個查看該命令請求的其余幾個操作。接下來的三個 JOIN 中, 每個都會增加初始的 Customer SELECT,還將使用聚集索引訪問表並返回單獨一 行。
通過此調查您可以看到,處理此查詢總共需要訪問四行,每一行來自 AdventureWorks 數據庫中的一個不同表。每次訪問均使用表的唯一主鍵有效執 行。
與此類似,您可以使用 TIP 查看 orderquery 代碼的 SQL 命令並將其提供 給 SQL Server Management Studio 以查看其執行計劃(參見圖 6)。此查詢 使用 CustomerID 作為外鍵訪問一個名為 OrderHdr 的表,因此需要訪問 SalesOrderHeaderID 上的普通非聚集索引以及聚集索引。
圖 6 orderquery 的執行計劃
這個特殊的查詢實例返回九行。LINQ to SQL 代碼中的 orderby 子句轉換 為 SQL ORDER BY 子句,使得在 SELECT 的結果集上執行一個額外的排序操作。根據 SQL Server 計劃優化程序的估算,此操作占用了請求總執行成本的 40%。
選擇分析上下文
TIP 旨在為現有 Visual Studio 分析方法提供補充,用於收集數據層交互上 的特定度量。TIP 是輔助的數據收集工具,必須指定主要分析方法才能收集數 據。對於任何使用 ADO.NET 與數據層通信的應用程序,可以在采樣、檢測和並 發分析運行期間收集 TIP 數據。
假設您需要為將收集 TIP 數據的應用程序選擇一種主要分析方法,您將使用 哪種方法? 現在讓我們了解選擇主要分析方法的一些注意事項。
在性能研究中是否主要關注與數據層交互相關的延遲? 如果是,則建議使用 采樣分析作為主要方法,因為此方法通常是干擾最少的一種分析形式。
如果在性能研究中主要關注的不是數據層延遲,請根據在當前上下文中哪種 分析方法能夠提供最合適的測量數據來做出選擇。例如,如果要研究與多線程 並發執行相關的問題,則收集並發數據。如果要研究與 CPU 密集型應用程序相 關的問題,則收集采樣數據。
如果您尚不熟悉數據層代碼,則可能需要從主要分析數據中獲取幫助,以找 到發起這些 ADO.NET 調用的准確代碼。TIP 在收集同步 ADO.NET 進程外調用 的計時信息時不捕獲調用堆棧。如果需要了解在應用程序中對 ADO.NET 方法進 行調用的位置,則檢測分析最為有用。采樣數據也會有所幫助,但其精度不如 檢測分析。
您可以選擇同時收集資源爭用數據和層交互度量,但收集爭用數據相比采樣 往往是較高開銷的函數,並且爭用數據對於確定發起特定 ADO.NET 調用的位置 一般沒有任何幫助。對於需要 .NET 內存分配分析(通常具有很大的影響)的 調查,通常也不會從收集層交互度量中獲益。
采樣分析
一般而言,在性能研究中數據層交互本身是主要關注對象。在本例中,選擇 采樣作為主要分析方法通常會獲得最佳結果。在本例中,由於這通常是對應用 程序性能影響最小的分析方法,因此首選采樣方法。在定位到發起對性能影響 最大的 ADO.NET 調用的源代碼時,采樣分析也會非常有用。
對於在采樣分析期間收集的指令執行樣本,在使用在進程外運行的數據層函 數時,通常不反映應用程序等待通過 ADO.NET 接口進行的同步調用完成所花費 的任何時間。在應用程序的執行線程等待這些進程外調用完成期間,將阻止應 用程序線程並且不對其記錄任何執行樣本。使用采樣時,了解哪些應用程序延 遲是由於對數據層的同步調用而造成的最佳方法是收集 TIP 數據。
TIP 使用的檢測在收集計時數據時不捕獲任何調用堆棧。因此,如果您對分 層應用程序進行分析並且不完全熟悉代碼,則可能難於准確確定發起數據層調用 的位置。采樣分析同樣也有助於確定應用程序代碼中對這些 ADO.NET 接口執行 調用的位置。如果應用程序頻繁調用 ADO.NET 接口,則很有可能會收集一些顯 示在 ADO.NET 模塊(包括 System.Data.dll 和 System.Data.Linq.dll)中用 時的樣本。
在檢查采樣數據並將其與層交互度量進行比較時,請記住,在等待同步數據 庫調用完成而阻止應用程序線程時,將不收集線程的任何采樣數據。樣本在執 行期間會累積,但不包括 TIP 明確測量的進程外延遲。不過,您可以預計在 ADO.NET 方法中收集的執行樣本與 TIP 觀察和測量的 ADO.NET 命令數之間存在 大致的關聯。在這些情況下,采樣分析有助於您定位到發出 TIP 測量和報告的 ADO.NET 調用的源代碼。
請注意,如果應用程序的 SQL 查詢返回很大的結果集,隨後這些結果集綁定 到窗體上的數據綁定控件,則您會在控件的 DataBind 方法中發現非常高的執行 采樣數。查看采樣分析中出現哪些 DataBind 方法也有助於定位到發起 ADO.NET 調用的源代碼。
檢測分析
收集檢測分析時,檢測記錄的方法的計時數據已經包括了方法中等待進程外 調用完成的任意用時。對於選定進行檢測的應用程序方法,將測量其各個方法 進入和退出的時間,以此來收集在檢測分析中記錄的計時數據。對於應用程序 中使用 ADO.NET 調用與數據層交互的方法,其計時數據中已隱式包含了執行任 何進程外調用的延遲。
從 TIP 收集的計時數據單獨明確地確定和測量進程外延遲。通過層交互分 析測量的延遲,應該為在檢測分析運行期間測量的方法內部總用時的子集。了 解這一點以後,您應該能夠將層交互分析的計時數據與在檢測分析的方法級別上 收集的計時數據進行匹配,以查明發起數據層調用的方法。
如果使用方法級別的檢測足以確定應用程序中發起任意 ADO.NET 調用的位置 ,則可以毫不猶豫地使用檢測分析。但是,檢測分析相比采樣分析的干擾通常 會大得多,這會帶來較大的開銷,並往往會生成非常大的 .vsp 收集文件。此 外,如果應用程序使用多次調用 ADO.NET 函數的方法,則檢測收集的數據僅能 幫助您定位到方法級別,而無法區分內嵌到單個方法內部的多個 ADO.NET 調用 。
獲取更多數據
構建多層應用程序這種設計模式有助於提高可靠性和可伸縮性,但當應用程 序組件在不同計算機上執行時會帶來性能監視上的困難。
對於多層應用程序而言,一個未涵蓋其相互關聯的各層的簡單視圖不能提供 完整的性能信息。正如您所看到的,TIP 可以提供其他方法所無法提供的關鍵 計時數據。如本文中的示例建議,此計時數據與通過其他標准數據庫管理工具 得到的性能數據一起使用時可以發揮更大的作用。