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.Information
wartość , 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, usługa aplikacja systemu Azure Insights zapewnia takie zaawansowane monitorowanie poza urządzeniem, 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 znajduje się w samouczku dotyczącym wydajności usługi Application Insights i na stronie analizy azure SQL.
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:
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.
Metryki
Powyższe sekcje koncentrowały się na sposobie uzyskiwania informacji o poleceniach i sposobie wykonywania tych poleceń w bazie danych. Oprócz tego ef uwidacznia zestaw metryk, które zapewniają więcej informacji niższego poziomu na temat tego, co dzieje się wewnątrz samej platformy EF, oraz sposobu korzystania z niej przez aplikację. Te metryki 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ę dotyczącą metryk 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):
- Ładowanie jednostek
- Ładowanie jednostek, brak śledzenia
- Klasyfikacja tylko projektu
- Obliczanie w bazie danych
[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:
Method | Ś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.