Wprowadzenie do wydajności
Wydajność bazy danych to obszerny i złożony temat obejmujący wiele elementów: bazę danych, sieć, sterownik bazy danych i warstwy dostępu do danych, takie jak platforma EF Core. Mimo że warstwy wysokiego poziomu i mapery O/RM, takie jak platforma EF Core, znacznie upraszczają tworzenie aplikacji i ułatwiają ich utrzymanie, czasami mogą być nieprzezroczyste, ukrywając wewnętrzne szczegóły krytyczne dla wydajności, takie jak wykonywany kod SQL. W tej sekcji postarano się przedstawić, jak osiągnąć dobrą wydajność przy użyciu platformy EF Core oraz jak uniknąć typowych pułapek, które mogą pogorszyć wydajność aplikacji.
Identyfikowanie wąskich gardeł i pomiary, pomiary, pomiary
Jak zawsze w przypadku wydajności, ważne jest, aby nie spieszyć się z optymalizacją, nie mając danych wskazujących na problem; jak powiedział kiedyś wielki Donald Knuth „Przedwczesna optymalizacja jest źródłem wszelkiego zła”. W sekcji Diagnostyka wydajności omówiono różne sposoby określania miejsc w logice bazy danych, w których aplikacja spędza najwięcej czasu, i wskazywania konkretnych problematycznych obszarów. Po zidentyfikowaniu powolnego zapytania można rozważyć rozwiązania: czy w bazie danych brakuje indeksu? Czy należy wypróbować inne wzorce wykonywania zapytań?
Zawsze samodzielnie przeprowadzaj testy porównawcze kodu i możliwe alternatywy — sekcja Diagnostyka wydajności zawiera przykładowy test porównawczy z biblioteką benchmarkDotNet, którego można użyć jako szablonu dla własnych testów porównawczych. Nie zakładaj, że ogólne, publicznie dostępne testy porównawcze mają zastosowanie do Twojego konkretnego przypadku użycia. Różne czynniki, takie jak opóźnienie bazy danych, złożoność zapytań i rzeczywiste ilości danych w tabelach, mogą mieć ogromny wpływ na to, które rozwiązanie będzie najlepsze. Na przykład wiele publicznych testów porównawczych jest wykonywanych w idealnych warunkach sieciowych, z niemal zerowym opóźnieniem do bazy danych, i z bardzo prostymi zapytaniami, które prawie nie wymagają przetwarzania (lub operacji we/wy dysku) po stronie bazy danych. Chociaż są one przydatne do porównywania obciążeń środowiska uruchomieniowego różnych warstw dostępu do danych, różnice, które ujawniają, zwykle są niewielkie w rzeczywistych zastosowaniach, gdy baza danych wykonuje rzeczywistą pracę i opóźnienia do bazy danych są znaczącym czynnikiem wydajności.
Aspekty wydajności dostępu do danych
Ogólną wydajność dostępu do danych można podzielić na następujące szerokie kategorie:
- Czysta wydajność bazy danych. W przypadku relacyjnej bazy danych platforma EF tłumaczy zapytania LINQ aplikacji na instrukcje SQL wykonywane przez bazę danych; te instrukcje SQL mogą działać mniej lub bardziej wydajnie. Odpowiedni indeks w odpowiednim miejscu może mieć ogromny wpływ na wydajność kodu SQL, a przepisanie zapytania LINQ może sprawić, że platforma EF wygeneruje lepsze zapytanie SQL.
- Sieciowy transfer danych. Jak w każdym systemie sieciowym ważne jest, aby ograniczyć ilość danych przesyłanych tam i z powrotem. Obejmuje to upewnienie się, że wysyłasz i ładujesz tylko te dane, których faktycznie będziesz potrzebować, ale także unikanie tak zwanego efektu „eksplozji kartezjańskiej” podczas ładowania powiązanych jednostek.
- Przesyłanie danych w sieci tam i z powrotem. Poza ilością danych przesyłanych tam i z powrotem czas potrzebny na wykonanie zapytania w bazie danych może wydawać się mały w porównaniu z czasem podróży pakietów tam i z powrotem między aplikacją a bazą danych. Obciążenie dwukierunkowe w dużym stopniu zależy od środowiska — im dalej znajduje się serwer bazy danych, tym większe opóźnienie i bardziej kosztowne przesyłanie danych tam i z powrotem. Wraz z pojawieniem się chmury aplikacje coraz częściej znajdują się daleko od bazy danych, a „gadatliwe” aplikacje, które wykonują zbyt wiele operacji dwukierunkowych, mają obniżoną wydajność. W związku z tym ważne jest, aby dokładnie rozumieć, kiedy aplikacja kontaktuje się z bazą danych, ile razy przesyła dane tam i z powrotem i czy można zminimalizować tę liczbę.
- Obciążenie środowiska uruchomieniowego EF. Na koniec sama platforma EF dodaje pewne obciążenie środowiska uruchomieniowego do operacji bazy danych: platforma EF musi kompilować zapytania kodu LINQ do SQL (chociaż zwykle powinno to być wykonywane tylko raz), śledzenie zmian dodaje pewne obciążenie (ale można je wyłączyć) itp. W praktyce obciążenie platformy EF dla rzeczywistych aplikacji może być niewielkie w większości przypadków, ponieważ czas wykonywania zapytań w bazie danych i opóźnienia sieci zajmują największą ilość czasu, ale ważne jest, aby wiedzieć, jakie opcje są dostępne i jak uniknąć niektórych pułapek.
Zobacz, co się dzieje pod maską
Platforma EF umożliwia deweloperom skoncentrowanie się na logice biznesowej przez generowanie kodu SQL, materializowanie wyników i wykonywanie innych zadań. Podobnie jak każda warstwa lub abstrakcja ma również tendencję do ukrywania tego, co dzieje się pod maską, na przykład podczas wykonywania rzeczywistych zapytań SQL. Wydajność nie musi być krytycznym aspektem każdej aplikacji, ale w aplikacjach, w których tak jest, należy zadbać o to, aby deweloper rozumiał, co robi platforma EF: wykonuje inspekcje wychodzących zapytań SQL, śledzi dane przesyłane tam i z powrotem, aby upewnić się, że nie występuje problem N+1, itp.
Pamięć podręczna poza bazą danych
Najbardziej efektywnym sposobem interakcji z bazą danych jest całkowity brak interakcji z nią. Innymi słowy, jeśli dostęp do bazy danych okazuje się być wąskim gardłem wydajności w aplikacji, warto buforować niektóre wyniki poza bazą danych, aby zminimalizować liczbę żądań. Buforowanie zwiększa złożoność, ale jest to szczególnie ważna część każdej skalowalnej aplikacji: mimo że warstwę aplikacji można łatwo skalować przez dodanie serwerów do obsługi zwiększonego obciążenia, skalowanie warstwy bazy danych jest zwykle znacznie bardziej skomplikowane.