效能診斷
本節討論在 EF 應用程式中偵測效能問題的方式,一旦識別出有問題的區域,如何進一步分析它們以找出根本問題。 請務必先仔細診斷並調查任何問題,再跳到任何結論,並避免假設問題的根本位置。
透過記錄識別緩慢的資料庫命令
在一天結束時,EF 會準備並執行要針對資料庫執行的命令;使用關係資料庫,這表示透過 ADO.NET 資料庫 API 執行 SQL 語句。 如果特定查詢花費太多時間(例如,因為遺漏索引),則可以藉由檢查命令執行記錄並觀察其實際花費的時間來發現。
EF 可讓您透過簡單的記錄或 Microsoft.Extensions.Logging,輕鬆地擷取命令運行時間:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0")
.LogTo(Console.WriteLine, LogLevel.Information);
}
在設定 LogLevel.Information
記錄層級時,EF 會針對每個命令執行發出記錄訊息,並花費下列時間:
info: 06/12/2020 09:12:36.117 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] = N'foo'
上述命令花費 4 毫秒。 如果特定命令需要超出預期,您就發現效能問題的可能罪魁禍首,現在可以專注於它,以瞭解其執行速度緩慢的原因。 命令記錄也可以顯示發生非預期資料庫往返的情況;這會顯示為只預期一個命令的多個命令。
警告
在生產環境中啟用命令執行記錄通常是個壞主意。 記錄本身會讓應用程式變慢,而且可能會快速建立大型記錄檔,以填滿伺服器的磁碟。 建議您只在短時間內持續記錄以收集資料 -- 同時仔細監視應用程式 - 或擷取生產前系統上的記錄數據。
將資料庫命令與 LINQ 查詢相互關聯
命令執行記錄的其中一個問題是,有時候很難將 SQL 查詢和 LINQ 查詢相互關聯:EF 所執行的 SQL 命令與產生的 LINQ 查詢看起來大不相同。 為了協助解決此困難,您可能想要使用 EF 的 查詢標籤 功能,這可讓您在 SQL 查詢中插入小型的識別批註:
var myLocation = new Point(1, 2);
var nearestPeople = (from f in context.People.TagWith("This is my spatial query!")
orderby f.Location.Distance(myLocation) descending
select f).Take(5).ToList();
標記會顯示在記錄中:
-- This is my spatial query!
SELECT TOP(@__p_1) [p].[Id], [p].[Location]
FROM [People] AS [p]
ORDER BY [p].[Location].STDistance(@__myLocation_0) DESC
通常值得以這種方式標記應用程式的主要查詢,讓命令執行記錄更容易立即讀取。
擷取效能數據的其他介面
EF 的記錄功能有各種替代方式可用來擷取命令運行時間,這可能更強大。 資料庫通常隨附自己的追蹤和效能分析工具,通常提供比簡單運行時間更豐富的資料庫特定資訊:實際的設定、功能和使用方式會因資料庫而異。
例如, SQL Server Management Studio 是功能強大的用戶端,可連線到您的 SQL Server 實例,並提供寶貴的管理和效能資訊。 超出本節討論詳細數據的範圍,但值得提及的兩項功能是 活動監視器,其提供伺服器活動的即時儀錶板(包括最昂貴的查詢),以及 擴充事件 (XEvent) 功能,其可讓您定義任意數據擷取會話,以根據您的確切需求量身打造。 有關監視 的 SQL Server 檔提供這些功能和其他功能的詳細資訊。
擷取效能數據的另一種方法是透過 DiagnosticSource
介面收集EF或資料庫驅動程式自動發出的信息,然後分析該數據或在儀錶板上顯示。 如果您使用 Azure,則 Azure 應用程式 Insights 會立即提供如此強大的監視功能,在分析 Web 要求的速度時整合資料庫效能和查詢運行時間。 如需詳細資訊,請參閱 Application Insights 效能教學課程和 Azure SQL 分析頁面。
檢查查詢執行計劃
一旦您找出需要優化的問題查詢,下一個步驟通常是分析查詢的執行 計劃。 當資料庫收到 SQL 語句時,它們通常會產生計劃的執行方式;這有時需要根據已定義索引、數據表中有多少數據等進行複雜的決策(順便說一句,計劃本身通常應該在伺服器快取,以獲得最佳效能)。 關係資料庫通常會為使用者提供查看查詢計劃的方式,以及查詢不同部分的計算成本:對於改善查詢而言,這是無價的。
若要開始使用 SQL Server,請參閱查詢執行計劃的檔。 典型的分析工作流程是使用 SQL Server Management Studio,貼上透過上述其中一種方法識別的慢速查詢 SQL,併 產生圖形化執行計劃:
雖然執行計劃一開始可能很複雜,但值得花點時間熟悉這些計劃。 請務必注意與計劃每個節點相關聯的成本,並識別各種節點中索引的使用方式(或不使用)。
雖然上述資訊專屬於 SQL Server,但其他資料庫通常會以類似的視覺效果提供相同種類的工具。
重要
資料庫有時會根據資料庫中的實際數據產生不同的查詢計劃。 例如,如果數據表只包含幾個數據列,資料庫可能會選擇不使用該數據表上的索引,而是改為執行完整數據表掃描。 如果在測試資料庫上分析查詢計劃,請一律確定它包含與生產系統類似的數據。
計量
上述各節著重於如何取得命令的相關信息,以及如何在資料庫中執行這些命令。 此外,EF 也會公開一組 計量 ,以提供有關 EF 本身內所發生狀況的更較低層級資訊,以及您的應用程式如何使用它。 這些計量對於診斷特定效能問題和效能異常非常有用,例如 導致常數重新編譯的查詢快取問題 、未公開的 DbContext 流失和其他問題。
如需詳細資訊,請參閱 EF 計量的專用頁面。
使用EF Core進行效能評定
在一天結束的時候,您有時需要知道撰寫或執行查詢的特定方式是否比另一種快。 請務必永遠不要假設或推測答案,而且很容易將快速基準放在一起以取得答案。 撰寫基準時,強烈建議使用已知的 BenchmarkDotNet 連結庫,這會處理使用者在嘗試撰寫自己的基準檢驗時遇到的許多陷阱:您是否執行了一些熱身反覆運算? 您的基準檢驗實際執行了多少個反覆專案,以及原因為何? 讓我們看看 EF Core 的基準檢驗外觀。
提示
您可以在這裡取得下列來源的完整基準檢驗專案。 建議您複製它,並將其作為您自己的基準範本使用。
作為簡單的基準檢驗案例,讓我們比較下列不同方法來計算資料庫中所有部落格的平均排名:
- 載入所有實體、加總其個別排名,並計算平均值。
- 與上述相同,只會使用非追蹤查詢。 這應該會更快,因為不會執行身分識別解析,而且不會針對變更追蹤的目的建立快照集。
- 藉由只投射排名,避免載入整個 Blog 實體實例。 可節省我們傳輸 Blog 實體類型的其他不必要的數據行。
- 將它納入查詢的一部分,以計算資料庫中的平均值。 這應該是最快的方式,因為資料庫中會計算所有專案,而且只會將結果傳送回用戶端。
使用 BenchmarkDotNet 時,您可以撰寫程式代碼,以作為簡單的方法進行基準檢驗,就像單元測試一樣,BenchmarkDotNet 會自動針對足夠的反覆專案執行每個方法,可靠地測量其花費的時間和配置多少記憶體。 以下是不同的方法(您可以在這裡看到完整的基準檢驗程序代碼):
[Benchmark]
public double LoadEntities()
{
var sum = 0;
var count = 0;
using var ctx = new BloggingContext();
foreach (var blog in ctx.Blogs)
{
sum += blog.Rating;
count++;
}
return (double)sum / count;
}
結果如下,如 BenchmarkDotNet 所列印:
方法 | 平均數 | 錯誤 | StdDev | 中間值 | 比例 | RatioSD | Gen 0 | 第 1 代 | 第 2 代 | 已配置 |
---|---|---|---|---|---|---|---|---|---|---|
LoadEntities | 2,860.4 我們 | 54.31 我們 | 93.68 我們 | 2,844.5 我們 | 4.55 | 0.33 | 210.9375 | 70.3125 | - | 1309.56 KB |
LoadEntitiesNoTracking | 1,353.0 我們 | 21.26 我們 | 18.85 我們 | 1,355.6 我們 | 2.10 | 0.14 | 87.8906 | 3.9063 | - | 540.09 KB |
ProjectOnlyRanking | 910.9 我們 | 20.91 我們 | 61.65 我們 | 892.9 我們 | 1.46 | 0.14 | 41.0156 | 0.9766 | - | 252.08 KB |
CalculateInDatabase | 627.1 我們 | 14.58 我們 | 42.54 我們 | 626.4 我們 | 1.00 | 0.00 | 4.8828 | - | - | 33.27 KB |
注意
當方法具現化和處置 方法內的內容時,這些作業會計入基準檢驗,不過嚴格來說,它們不屬於查詢程式的一部分。 如果目標是將兩個替代專案彼此比較(因為內容具現化和處置相同),並且為整個作業提供更全面的測量,這應該無關緊要。
BenchmarkDotNet 的其中一個限制是,它會測量您所提供方法的簡單、單個線程效能,因此不適合對並行案例進行效能評定。
重要
在基準檢驗時,請務必在資料庫中有類似生產數據的數據,否則基準檢驗結果可能不會代表實際執行效能。