Diagnostyka wydajności

W tej sekcji omówiono sposoby wykrywania problemów z wydajnością w aplikacji EF, a po zidentyfikowaniu problematycznego obszaru można je dalej analizować w celu zidentyfikowania głównego problemu. Ważne jest, aby dokładnie zdiagnozować i zbadać wszelkie problemy przed przejściem do jakichkolwiek wniosków i uniknąć przy założeniu, gdzie znajduje się główny problem.

Identyfikowanie wolnych poleceń bazy danych za pomocą rejestrowania

Na koniec dnia program EF przygotowuje i wykonuje polecenia do wykonania względem bazy danych; w przypadku relacyjnej bazy danych oznacza to wykonywanie instrukcji SQL za pośrednictwem interfejsu API bazy danych ADO.NET. Jeśli pewne zapytanie zajmuje zbyt dużo czasu (np. z powodu braku indeksu), można to zobaczyć, sprawdzając dzienniki wykonywania poleceń i obserwując, jak długo trwają.

Program EF ułatwia przechwytywanie czasów wykonywania poleceń za pośrednictwem prostego rejestrowania lub 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);
}

Gdy poziom rejestrowania jest ustawiony na LogLevel.Informationwartość , program EF emituje komunikat dziennika dla każdego wykonania polecenia z upływem czasu:

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'

Powyższe polecenie zajęło 4 milisekundy. Jeśli określone polecenie zajmuje więcej niż oczekiwano, znaleziono możliwego winowajcę problemu z wydajnością i możesz teraz skupić się na nim, aby zrozumieć, dlaczego działa powoli. Rejestrowanie poleceń może również ujawnić przypadki, w których są wykonywane nieoczekiwane przejazdy bazy danych; Spowoduje to wyświetlenie wielu poleceń, w których oczekiwano tylko jednego polecenia.

Ostrzeżenie

Pozostawienie rejestrowania wykonywania polecenia włączonego w środowisku produkcyjnym jest zwykle złym pomysłem. Rejestrowanie spowalnia aplikację i może szybko utworzyć ogromne pliki dziennika, które mogą wypełnić dysk serwera. Zaleca się rejestrowanie tylko przez krótki czas w celu zebrania danych — podczas dokładnego monitorowania aplikacji — lub przechwytywania danych rejestrowania w systemie przedprodukcyjnym.

Korelowanie poleceń bazy danych z zapytaniami LINQ

Jednym z problemów z rejestrowaniem wykonywania poleceń jest to, że czasami trudno jest skorelować zapytania SQL i zapytania LINQ: polecenia SQL wykonywane przez program EF mogą wyglądać bardzo inaczej niż zapytania LINQ, z których zostały wygenerowane. Aby ułatwić ten problem, możesz użyć funkcji tagów zapytań ef, która umożliwia wstrzyknięcie małego komentarza do zapytania 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();

Tag jest wyświetlany w dziennikach:

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

Często warto tagować główne zapytania aplikacji w ten sposób, aby dzienniki wykonywania poleceń można było od razu odczytać.

Inne interfejsy do przechwytywania danych wydajności

Istnieją różne alternatywy dla funkcji rejestrowania ef do przechwytywania czasów wykonywania poleceń, które mogą być bardziej zaawansowane. Bazy danych zwykle zawierają własne narzędzia do śledzenia i analizy wydajności, które zwykle zapewniają znacznie bogatsze informacje specyficzne dla bazy danych poza prostymi czasami wykonywania; rzeczywista konfiguracja, możliwości i użycie różnią się znacznie w różnych bazach danych.

Na przykład program SQL Server Management Studio to zaawansowany klient, który może łączyć się z wystąpieniem programu SQL Server i udostępniać cenne informacje o zarządzaniu i wydajności. Wykracza poza zakres tej sekcji, aby przejść do szczegółów, ale dwie możliwości, o których warto wspomnieć, to Monitor aktywności, który zapewnia dynamiczny pulpit nawigacyjny działania serwera (w tym najdroższe zapytania) i funkcję zdarzeń rozszerzonych (XEvent), która umożliwia definiowanie dowolnych sesji przechwytywania danych, które mogą być dostosowane do konkretnych potrzeb. Dokumentacja programu SQL Server dotycząca monitorowania zawiera więcej informacji na temat tych funkcji, a także innych.

Innym podejściem do przechwytywania danych wydajności jest zbieranie informacji automatycznie emitowanych przez program EF lub sterownik bazy danych za pośrednictwem interfejsu DiagnosticSource , a następnie analizowanie tych danych lub wyświetlanie ich na pulpicie nawigacyjnym. Jeśli korzystasz z platformy Azure, aplikacja systemu Azure Szczegółowe informacje zapewnia tak zaawansowane monitorowanie gotowe do użycia, integrując czasy wydajności bazy danych i wykonywania zapytań w analizie, jak szybko są obsługiwane żądania internetowe. Więcej informacji na ten temat można znaleźć w samouczku dotyczącym wydajności usługi Application Szczegółowe informacje oraz na stronie analizy sql azure.

Inspekcja planów wykonywania zapytań

Po określeniu problematycznego zapytania wymagającego optymalizacji następny krok zwykle analizuje plan wykonania zapytania. Gdy bazy danych otrzymują instrukcję SQL, zwykle tworzą plan wykonania tego planu; Czasami wymaga to skomplikowanego podejmowania decyzji na podstawie tego, które indeksy zostały zdefiniowane, ile danych istnieje w tabelach itp. (nawiasem mówiąc, sam plan powinien być zwykle buforowany na serwerze w celu uzyskania optymalnej wydajności). Relacyjne bazy danych zwykle umożliwiają użytkownikom wyświetlanie planu zapytania wraz z obliczanym kosztem dla różnych części zapytania; jest to bezcenne w przypadku ulepszania zapytań.

Aby rozpocząć pracę z programem SQL Server, zapoznaj się z dokumentacją dotyczącą planów wykonywania zapytań. Typowym przepływem pracy analizy jest użycie programu SQL Server Management Studio, wklejenie kodu SQL wolnego zapytania zidentyfikowanego za pomocą jednego z powyższych środków i utworzenie graficznego planu wykonywania:

Display a SQL Server execution plan

Chociaż plany wykonywania mogą wydawać się skomplikowane na początku, warto poświęcić trochę czasu na zapoznanie się z nimi. Szczególnie ważne jest, aby zanotować koszty związane z każdym węzłem planu oraz określić sposób użycia indeksów (lub nie) w różnych węzłach.

Chociaż powyższe informacje są specyficzne dla programu SQL Server, inne bazy danych zwykle udostępniają tego samego rodzaju narzędzia z podobną wizualizacją.

Ważne

Bazy danych czasami generują różne plany zapytań w zależności od rzeczywistych danych w bazie danych. Jeśli na przykład tabela zawiera tylko kilka wierszy, baza danych może nie używać indeksu w tej tabeli, ale zamiast tego przeprowadzić pełne skanowanie tabeli. W przypadku analizowania planów zapytań w testowej bazie danych zawsze upewnij się, że zawiera ona dane podobne do systemu produkcyjnego.

Liczniki zdarzeń

Powyższe sekcje koncentrowały się na sposobie uzyskiwania informacji o poleceniach i sposobie wykonywania tych poleceń w bazie danych. Oprócz tego program EF uwidacznia zestaw liczników zdarzeń, które zapewniają bardziej niższe informacje na temat tego, co dzieje się wewnątrz samej platformy EF oraz sposobu korzystania z niej przez aplikację. Te liczniki mogą być bardzo przydatne do diagnozowania określonych problemów z wydajnością i anomalii wydajności, takich jak problemy z buforowaniem zapytań, które powodują stałe ponowne komponowanie, niedysponowane przecieki dbContext i inne.

Aby uzyskać więcej informacji, zobacz dedykowaną stronę liczników zdarzeń platformy EF.

Benchmarking with EF Core

Na koniec dnia czasami trzeba wiedzieć, czy określony sposób pisania lub wykonywania zapytania jest szybszy niż inny. Ważne jest, aby nigdy nie zakładać ani spekulować odpowiedzi i niezwykle łatwo jest zebrać szybki punkt odniesienia, aby uzyskać odpowiedź. Podczas pisania testów porównawczych zdecydowanie zaleca się użycie dobrze znanej biblioteki BenchmarkDotNet , która obsługuje wiele pułapek napotykanych przez użytkowników podczas próby pisania własnych testów porównawczych: czy wykonano kilka iteracji rozgrzewki? Ile iteracji faktycznie uruchamia twój test porównawczy i dlaczego? Przyjrzyjmy się temu, jak wygląda test porównawczy z platformą EF Core.

Napiwek

Pełny projekt porównawczy dla poniższego źródła jest dostępny tutaj. Zachęcamy do skopiowania go i użycia go jako szablonu dla własnych testów porównawczych.

Jako prosty scenariusz porównawczy porównajmy następujące różne metody obliczania średniej klasyfikacji wszystkich blogów w naszej bazie danych:

  • Załaduj wszystkie jednostki, zsumuj swoje indywidualne rankingi i oblicz średnią.
  • Tak samo jak powyżej, należy używać tylko zapytania śledzenia. Powinno to być szybsze, ponieważ rozpoznawanie tożsamości nie jest wykonywane, a jednostki nie są migawkowane do celów śledzenia zmian.
  • Unikaj ładowania całych wystąpień jednostek bloga, projektując tylko klasyfikację. Element pozwala nam przenieść inne, niepotrzebne kolumny typu jednostki Blog.
  • Oblicz średnią w bazie danych, tworząc ją jako część zapytania. Powinien to być najszybszy sposób, ponieważ wszystko jest obliczane w bazie danych i tylko wynik jest transferowany z powrotem do klienta.

W narzędziu BenchmarkDotNet napiszesz kod, który ma zostać przetestowany jako prosta metoda — podobnie jak test jednostkowy — i BenchmarkDotNet automatycznie uruchamia każdą metodę dla wystarczającej liczby iteracji, niezawodnie mierząc, jak długo trwa i ile pamięci jest przydzielona. Oto różne metody (pełny kod testu porównawczego można zobaczyć tutaj):

[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;
}

Wyniki są poniższe, jak wydrukowano w witrynie BenchmarkDotNet:

Metoda Średnia Błąd StdDev Mediana Współczynnik RatioSD Gen 0 Pierwszej generacji Drugiej generacji Alokowane
Jednostki obciążenia 2860.4 nas 54.31 us 93.68 us 2844,5 nas 4,55 0.33 210.9375 70.3125 - 1309,56 KB
LoadEntitiesNoTracking 1353.0 21.26 nas 18.85 us 1355.6 2.10 0.14 87.8906 3.9063 - 540.09 KB
ProjectOnlyRanking 910.9 20.91 us 61.65 nas 892.9 us 1,46 0.14 41.0156 0.9766 - 252.08 KB
CalculateInDatabase 627.1 nas 14.58 nas 42.54 nas 626.4 nas 1,00 0.00 4.8828 - - 33.27 KB

Uwaga

Ponieważ metody tworzą wystąpienie i usuwają kontekst w metodzie, te operacje są liczone dla testu porównawczego, chociaż ściśle mówiąc, nie są częścią procesu wykonywania zapytań. Nie powinno to mieć znaczenia, czy celem jest porównanie dwóch alternatyw ze sobą (ponieważ tworzenie i usuwanie kontekstu jest takie samo) i daje bardziej całościowy pomiar dla całej operacji.

Jednym z ograniczeń benchmarkDotNet jest to, że mierzy prostą, jednowątkową wydajność zapewnianych metod i dlatego nie nadaje się do testowania współbieżnych scenariuszy.

Ważne

Zawsze upewnij się, że dane w bazie danych są podobne do danych produkcyjnych podczas testów porównawczych. W przeciwnym razie wyniki testu porównawczego mogą nie reprezentować rzeczywistej wydajności w środowisku produkcyjnym.