Sdílet prostřednictvím


Diagnostika výkonu

Tato část popisuje způsoby detekce problémů s výkonem v aplikaci EF a po zjištění problematické oblasti, jak je dále analyzovat a identifikovat původní problém. Před přechodem na závěry je důležité pečlivě diagnostikovat a prošetřit všechny problémy a vyhnout se předpokladu, že se jedná o kořen problému.

Identifikace pomalých databázových příkazů prostřednictvím protokolování

Na konci dne ef připraví a spustí příkazy, které se mají spouštět v databázi; relační databáze znamená spouštění příkazů SQL prostřednictvím rozhraní API ADO.NET databáze. Pokud určitý dotaz trvá příliš dlouho (např. protože chybí index), můžete ho zjistit kontrolou protokolů spouštění příkazů a sledováním doby, po kterou skutečně trvá.

EF usnadňuje zaznamenávání doby provádění příkazů prostřednictvím jednoduchého protokolování nebo 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);
}

Když je úroveň protokolování nastavena na LogLevel.Information, EF vygeneruje zprávu protokolu pro každé spuštění příkazu s časem potřebným k provedení:

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'

Výše uvedený příkaz trval 4 milisekundy. Pokud určitý příkaz trvá déle, než se čekalo, našli jste možnou příčinu problému s výkonem a teď se na něj můžete zaměřit, abyste pochopili, proč běží pomalu. Protokolování příkazů může také odhalit případy, kdy probíhá neočekávané zaokrouhlení databáze; zobrazí se jako více příkazů, u kterých se očekává pouze jeden.

Upozorňující

Ponechání protokolování spouštění příkazů povolené v produkčním prostředí je obvykle špatný nápad. Samotné protokolování zpomalí vaši aplikaci a může rychle vytvořit obrovské soubory protokolu, které můžou zaplnit disk serveru. Doporučuje se uchovávat protokolování pouze po krátkou dobu, aby se shromáždila data – při pečlivém monitorování aplikace – nebo zachytávání dat protokolování v předprodukčním systému.

Korelace databázových příkazů s dotazy LINQ

Jedním z problémů s protokolováním spouštění příkazů je, že je někdy obtížné korelovat dotazy SQL a dotazy LINQ: příkazy SQL spouštěné efem můžou vypadat velmi jinak než dotazy LINQ, ze kterých byly generovány. Pro usnadnění tohoto problému můžete chtít použít funkci značek dotazů EF, která umožňuje vložit do dotazu SQL malý komentář s identifikací:

var myLocation = new Point(1, 2);
var nearestPeople = await (from f in context.People.TagWith("This is my spatial query!")
                     orderby f.Location.Distance(myLocation) descending
                     select f).Take(5).ToListAsync();

Značka se zobrazí v protokolech:

-- 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

Často stojí za označování hlavních dotazů aplikace tímto způsobem, aby byly protokoly spouštění příkazů čitelnější.

Další rozhraní pro zachytávání dat o výkonu

Existují různé alternativy funkce protokolování EF pro zachycení doby provádění příkazů, což může být výkonnější. Databáze obvykle obsahují vlastní nástroje pro trasování a analýzu výkonu, které obvykle poskytují mnohem bohatší informace specifické pro databázi nad rámec jednoduchých časů provádění; skutečné nastavení, možnosti a využití se výrazně liší v různých databázích.

SQL Server Management Studio je například výkonný klient, který se může připojit k vaší instanci SQL Serveru a poskytovat cenné informace o správě a výkonu. Podrobnosti jsou nad rámec této části, ale dvě funkce, které stojí za zmínku, jsou Monitorování aktivit, které poskytuje živý řídicí panel aktivity serveru (včetně nejdražších dotazů) a funkci Rozšířené události (XEvent), která umožňuje definovat libovolné relace zachytávání dat, které se dají přizpůsobit vašim přesným potřebám. Dokumentace k SQL Serveru o monitorování poskytuje další informace o těchto funkcích a dalších funkcích.

Dalším přístupem k zachytávání dat o výkonu je shromažďování informací automaticky vygenerovaných efem nebo ovladačem databáze prostřednictvím DiagnosticSource rozhraní a následná analýza těchto dat nebo jejich zobrazení na řídicím panelu. Pokud používáte Azure, pak Aplikace Azure lication Insights poskytuje takové výkonné monitorování, integrace výkonu databáze a doby provádění dotazů v analýze toho, jak rychle se obsluhují webové požadavky. Další informace o tom najdete v kurzu výkonu Application Insights a na stránce Azure SQL Analytics.

Kontrola plánů spouštění dotazů

Po určení problematického dotazu, který vyžaduje optimalizaci, je dalším krokem obvykle analýza plánu provádění dotazu. Když databáze obdrží příkaz SQL, obvykle vytvoří plán, jak se má tento plán provést; někdy to vyžaduje složité rozhodování na základě toho, které indexy byly definovány, kolik dat existuje v tabulkách atd. (mimochodem samotný plán by měl být uložen v mezipaměti na serveru pro zajištění optimálního výkonu). Relační databáze obvykle poskytují uživatelům způsob, jak zobrazit plán dotazu spolu s vypočítanými náklady na různé části dotazu; to je neocenitelné pro zlepšení vašich dotazů.

Pokud chcete začít s SQL Serverem, přečtěte si dokumentaci k plánům provádění dotazů. Typickým pracovním postupem analýzy by bylo použití aplikace SQL Server Management Studio, vložení SQL pomalého dotazu identifikovaného jedním z výše uvedených prostředků a vytvoření grafického plánu provádění:

Zobrazení plánu spuštění SQL Serveru

I když se plány provádění můžou zpočátku zdát složité, stojí za to trochu času se s nimi seznámit. Je zvlášť důležité si uvědomit náklady spojené s každým uzlem plánu a určit, jak se indexy používají (nebo ne) v různých uzlech.

Výše uvedené informace jsou specifické pro SQL Server, ale jiné databáze obvykle poskytují stejný druh nástrojů s podobnou vizualizací.

Důležité

Databáze někdy generují různé plány dotazů v závislosti na skutečných datech v databázi. Pokud například tabulka obsahuje jenom několik řádků, databáze se může rozhodnout, že nebude používat index v této tabulce, ale místo toho provést úplnou kontrolu tabulky. Pokud analyzujete plány dotazů na testovací databázi, vždy se ujistěte, že obsahuje data podobná vašemu produkčnímu systému.

Metriky

Výše uvedené části se zaměřují na to, jak získat informace o příkazech a jak se tyto příkazy spouští v databázi. Kromě toho EF zveřejňuje sadu metrik , které poskytují více informací na nižší úrovni o tom, co se děje uvnitř samotného EF a jak ji vaše aplikace používá. Tyto metriky můžou být velmi užitečné při diagnostice konkrétních problémů s výkonem a anomálií výkonu, jako jsou problémy s ukládáním dotazů do mezipaměti, které způsobují neustálé rekompilace, nedispozované úniky dbContext a další.

Další informace najdete na vyhrazené stránce metrik EF .

Srovnávací testy s EF Core

Na konci dne někdy potřebujete vědět, jestli je určitý způsob psaní nebo provádění dotazu rychlejší než jiný. Je důležité nikdy nepředpokládat ani spekulovat odpověď a je velmi snadné sestavit rychlý srovnávací test pro získání odpovědi. Při psaní srovnávacích testů důrazně doporučujeme použít dobře známou knihovnu BenchmarkDotNet , která zpracovává mnoho nástrah uživatelů při pokusu o zápis vlastních srovnávacích testů: provedli jste některé iterace zahřátí? Kolik iterací srovnávací test skutečně běží a proč? Pojďme se podívat, jak vypadá srovnávací test s EF Core.

Tip

Úplný srovnávací projekt pro níže uvedený zdroj je k dispozici zde. Doporučujeme ho zkopírovat a použít jako šablonu pro vlastní srovnávací testy.

Jako jednoduchý scénář srovnávacího testu porovnáme následující různé metody výpočtu průměrného hodnocení všech blogů v naší databázi:

  • Načtěte všechny entity, sečtěte jejich jednotlivá hodnocení a vypočítejte průměr.
  • Totéž jako v předchozím případě používejte pouze nesledovaný dotaz. To by mělo být rychlejší, protože se neprovádí řešení identit a entity nejsou snímkované pro účely sledování změn.
  • Vyhněte se načítání všech instancí entit blogu tím, že promítáte pouze hodnocení. Uloží nás z přenosu ostatních nepotřebných sloupců typu entity Blog.
  • Spočítejte průměr v databázi tak, že ho vytvoříte jako součást dotazu. To by mělo být nejrychlejší způsob, protože všechno se vypočítá v databázi a přenese se zpět do klienta.

Pomocí BenchmarkDotNet napíšete kód, který se má testovat jako jednoduchá metoda – stejně jako test jednotek – a BenchmarkDotNet automaticky spustí každou metodu pro dostatečný počet iterací, spolehlivě měří, jak dlouho trvá a kolik paměti je přiděleno. Tady jsou různé metody (úplný kód srovnávacího testu můžete vidět tady):

[Benchmark]
public async Task<double> LoadEntities()
{
    var sum = 0;
    var count = 0;
    using var ctx = new BloggingContext();
    await foreach (var blog in ctx.Blogs.AsAsyncEnumerable())
    {
        sum += blog.Rating;
        count++;
    }

    return (double)sum / count;
}

Výsledky jsou uvedené níže, jak je vytištěno společností BenchmarkDotNet:

metoda Střední hodnota Chyba Směrodatná odchylka Medián Koeficient RatioSD Gen 0 Gen 1 Gen 2 Přiděleno
LoadEntities 2 860,4 us 54.31 nás 93,68 nás 2 844,5 us 4.55 0.33 210.9375 70.3125 - 1309.56 KB
LoadEntitiesNoTracking 1 353.0 21.26 nás 18,85 us 1 355,6 nás 2.10 0,14 87.8906 3.9063 - 540.09 KB
ProjectOnlyRanking 910.9 us 20.91 nás 61,65 nás 892,9 nás 1,46 0,14 41.0156 0.9766 - 252.08 KB
CalculateInDatabase 627.1 us 14,58 nás 42,54 nás 626.4 us 1,00 0,00 4.8828 - - 33,27 kB

Poznámka:

Když metody vytvoří instanci a zlikvidují kontext v rámci metody, tyto operace se počítají pro srovnávací test, i když přísně řečeno nejsou součástí procesu dotazování. To by nemělo být důležité, pokud je cílem porovnat dvě alternativy k sobě navzájem (protože kontextová instance a odstranění jsou stejné) a poskytuje ucelenější měření pro celou operaci.

Jedním z omezení benchmarkDotNet je, že měří jednoduchý výkon metod, které poskytujete, s jedním vláknem, a proto není vhodný pro srovnávací testy souběžných scénářů.

Důležité

Vždy se ujistěte, že máte v databázi data podobná produkčním datům při srovnávacím testování, jinak výsledky srovnávacího testu nemusí představovat skutečný výkon v produkčním prostředí.