Share via


效能診斷

本節討論在 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,並 產生圖形化執行計畫

Display a SQL Server execution plan

雖然執行計畫一開始可能很複雜,但值得花點時間熟悉這些計畫。 請務必注意與計畫每個節點相關聯的成本,並識別各種節點中索引的使用方式(或不使用)。

雖然上述資訊專屬於 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 Median 比例 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 的其中一個限制是,它會測量您所提供方法的簡單、單一執行緒效能,因此不適合對並行案例進行效能評定。

重要

在基準測試時,請務必在資料庫中有類似生產資料的資料,否則基準測試結果可能不會代表實際執行效能。