Czy Entity Framework jest odpowiedni dla witryn o dużym natężeniu ruchu?


176

Czy Entity Framework 4 to dobre rozwiązanie dla publicznej witryny internetowej z potencjalnie 1000 odsłonami na sekundę?

W moim rozumieniu EF jest realnym rozwiązaniem dla większości mniejszych lub intranetowych stron internetowych, ale nie dałoby się łatwo skalować dla czegoś takiego jak popularna strona społeczności (wiem, że SO używa LINQ do SQL, ale .. Chciałbym więcej przykładów / dowodów. ..)

Teraz stoję na rozdrożu albo wyboru czystego podejścia ADO.NET, albo EF4. Czy uważasz, że lepsza wydajność programistów dzięki EF jest warta utraconej wydajności i szczegółowego dostępu do ADO.NET (z procedurami przechowywanymi)? Jakieś poważne problemy, z którymi może się spotkać strona o dużym ruchu, czy używała EF?

Z góry dziękuję.


1
Nie rozumiesz skalowania. Skalowanie oznacza uzyskanie 10-krotnej przepustowości po dodaniu 10-krotnej pojemności. Dlaczego EF miałby temu zapobiec? Dodaje stały współczynnik obciążenia do każdego obciążenia bazy danych.
usr

Odpowiedzi:


152

To zależy trochę od tego, ile potrzebujesz abstrakcji . Wszystko jest kompromisem; na przykład, EF i NHibernate wprowadzają dużą elastyczność do reprezentowania danych w ciekawych i egzotycznych modeli - ale w wyniku czego zrobić dodać napowietrznych. Zauważalny narzut.

Jeśli nie musisz mieć możliwości przełączania się między dostawcami baz danych i różnymi układami tabel dla poszczególnych klientów oraz jeśli dane są głównie odczytywane , i jeśli nie musisz mieć możliwości korzystania z tego samego modelu w EF, SSRS , Usługi danych ADO.NET itp. - jeśli więc chcesz mieć absolutną wydajność jako kluczową miarę, możesz zrobić znacznie gorzej niż spojrzeć na eleganckie . W naszych testach opartych zarówno na LINQ-to-SQL, jak i EF, stwierdzamy, że EF jest znacznie wolniejszy pod względem wydajności odczytu surowego, prawdopodobnie z powodu warstw abstrakcji (między modelem pamięci itp.) I materializacji.

Tutaj, w SO, jesteśmy obsesyjno-kompulsywni w kwestii surowej wydajności i cieszymy się, że wykorzystujemy hit rozwojowy polegający na utracie abstrakcji w celu przyspieszenia. Jako takie nasze podstawowe narzędzie do wyszukiwania w bazie danych jest eleganckie . To pozwala nam nawet korzystać z naszego wcześniej istniejącego modelu LINQ-SQL, ale po prostu: jest on o wiele szybszy. W testach wydajnościowych jest to dokładnie taka sama wydajność jak ręczne pisanie całego kodu ADO.NET (parametry, czytniki danych itp.), Ale bez ryzyka błędnej nazwy kolumny. Jest jednak oparty na SQL (chociaż chętnie używa SPROC, jeśli jest to twoja wybrana trucizna). Zaletą jest to, że nie ma żadnego dodatkowego przetwarzania zaangażowany, ale to jest system dla ludzi, którzy lubią SQL. Co uważam: niezła rzecz!

Typowym zapytaniem może być na przykład:

int customerId = ...
var orders = connection.Query<Order>(
    "select * from Orders where CustomerId = @customerId ",
    new { customerId }).ToList();

co jest wygodne, bezpieczne dla iniekcji itp. - ale bez mnóstwa lepkich czytników danych. Pamiętaj, że chociaż może obsługiwać zarówno poziome, jak i pionowe partycje, aby załadować złożone struktury, nie będzie obsługiwał leniwego ładowania (ale: jesteśmy wielkimi fanami bardzo wyraźnego ładowania - mniej niespodzianek).

Uwaga w tej odpowiedzi nie twierdzę, że EF nie nadaje się do pracy w dużych ilościach; po prostu: wiem, że szykowny jest do tego.


25
+1 dla eleganckiego. Używanie złożonego ORM do odczytu modeli jest po prostu niepotrzebne. Podejście, które teraz przyjmujemy, polega na użyciu ORM dla naszego modelu domeny (w którym fantazyjne rzeczy ORM są naprawdę przydatne) i elegancji dla naszego modelu odczytu. To sprawia, że ​​superszybkie aplikacje.

2
@Marc, dziękuję za świetną odpowiedź - mogę w końcu podjąć decyzję z pewnością siebie! Na pewno później przyjrzy się bardziej elegancko. Naprawdę podoba mi się, jak to jest tylko jeden plik :)

3
Napisałem własną ORM. Jest wolny. Spojrzałem na elegancką i podobało mi się. Teraz używam eleganckiego do wszystkich moich odczytów i własnego ORM dla wstawek (który obsługuje FK, transakcje i wszystkie dobre rzeczy). To najłatwiejszy i najbardziej czytelny kod, jaki kiedykolwiek napisałem.

2
@ acidzombie24 dapper obsługuje transakcje, a część wkładu dapper (nie jest częścią wdrożenia nuget) zyskuje opcje wstawiania itp. Wystarczy wspomnieć o kompletności. Cieszę się, że elegancka była przydatna.
Marc Gravell

1
@anyname Nigdy nie stworzyłem kursu wideo na żaden temat; są tam jakieś filmy, ale nie przeze mnie. Zazwyczaj jestem osobą pisaną
Marc Gravell

217

Pytanie „którego ORM powinienem użyć” naprawdę dotyczy czubka ogromnej góry lodowej, jeśli chodzi o ogólną strategię dostępu do danych i optymalizację wydajności w aplikacji na dużą skalę.

Wszystkie następujące rzeczy (z grubsza w kolejności ważności) będą miały wpływ na przepustowość, a wszystkie z nich są obsługiwane (czasami na różne sposoby) przez większość głównych platform ORM:

  1. Projektowanie i utrzymanie baz danych

    Jest to, z szerokim marginesem, najważniejszy wyznacznik przepustowości aplikacji lub strony internetowej sterowanej danymi i często całkowicie ignorowany przez programistów.

    Jeśli nie użyjesz odpowiednich technik normalizacji, Twoja strona jest skazana na niepowodzenie. Jeśli nie masz kluczy podstawowych, prawie każde zapytanie będzie wolne. Jeśli użyjesz dobrze znanych anty-wzorców, takich jak używanie tabel dla par klucz-wartość (AKA Entity-Attribute-Value) bez powodu, rozbijesz liczbę fizycznych odczytów i zapisów.

    Jeśli nie skorzystasz z funkcji, które oferuje baza danych, takich jak kompresja strony, FILESTREAMpamięć masowa (dane binarne), SPARSEkolumny, hierarchyidhierarchie itd. (Wszystkie przykłady SQL Server), nie zobaczysz nigdzie w pobliżu wydajność, którą można zobaczyć.

    Powinieneś zacząć martwić się strategią dostępu do danych po zaprojektowaniu bazy danych i przekonaniu siebie, że jest ona tak dobra, jak to tylko możliwe, przynajmniej na razie.

  2. Chętni kontra Leniwi Ładowanie

    Większość ORM stosowała technikę zwaną leniwym ładowaniem relacji, co oznacza, że ​​domyślnie ładuje jedną jednostkę (wiersz tabeli) na raz i robi objazd do bazy danych za każdym razem, gdy musi załadować jedną lub wiele powiązanych (zagranicznych) klucz) wiersze.

    To nie jest dobra ani zła rzecz, raczej zależy to od tego, co faktycznie zrobimy z danymi i od tego, ile wiesz z góry. Czasami leniwe ładowanie jest absolutnie właściwe. Na przykład NHibernate może zdecydować, że w ogóle nie będzie pytać o nic i po prostu wygeneruje serwer proxy dla określonego identyfikatora. Jeśli wszystko, czego kiedykolwiek potrzebujesz, to sam identyfikator, dlaczego miałbyś prosić o więcej? Z drugiej strony, jeśli próbujesz wydrukować drzewo każdego elementu w 3-poziomowej hierarchii, leniwe ładowanie staje się operacją O (N²), co jest bardzo niekorzystne dla wydajności.

    Jedną z interesujących korzyści z używania „czystego SQL” (tj. Surowych zapytań / procedur przechowywanych ADO.NET) jest to, że w zasadzie zmusza cię do zastanowienia się, jakie dane są niezbędne do wyświetlenia danego ekranu lub strony. ORMs i cechy leniwy załadunku nie zapobiega cię od robienia tego, ale oni nie dają możliwość bycia ... cóż, ci leniwi i przypadkowego wybuchu liczbę zapytań wykonują. Musisz więc zrozumieć funkcje ładowania ORM i być zawsze czujnym, jeśli chodzi o liczbę zapytań wysyłanych do serwera dla każdego żądania strony.

  3. Buforowanie

    Wszystkie główne ORM utrzymują pamięć podręczną pierwszego poziomu, AKA „pamięć podręczną tożsamości”, co oznacza, że ​​jeśli dwukrotnie zażądasz tego samego bytu według jego identyfikatora, nie wymaga to drugiej podróży w obie strony, a także (jeśli poprawnie zaprojektowałeś bazę danych ) daje możliwość korzystania z optymistycznej współbieżności.

    Pamięć podręczna L1 jest dość nieprzezroczysta w L2S i EF, musisz w pewien sposób zaufać, że działa. NHibernate mówi o tym bardziej wyraźnie ( Get/ Loadvs. Query/ QueryOver). Tak długo, jak będziesz próbował zapytać według identyfikatora w jak największym stopniu, powinieneś być w porządku. Wiele osób zapomina o pamięci podręcznej L1 i wielokrotnie wyszukuje ten sam byt w kółko za pomocą czegoś innego niż jego identyfikator (tj. Pole odnośnika). Jeśli musisz to zrobić, powinieneś zapisać identyfikator, a nawet cały byt na przyszłe wyszukiwania.

    Istnieje również pamięć podręczna poziomu 2 („pamięć podręczna zapytań”). NHibernate ma to wbudowane. Linq do SQL i Entity Framework mają skompilowane zapytania , które mogą pomóc nieco zmniejszyć obciążenia serwera aplikacji, kompilując samo wyrażenie zapytania, ale nie buforuje danych. Wydaje się, że Microsoft uważa to za problem związany z aplikacją, a nie za dostęp do danych, i jest to główny słaby punkt zarówno L2S, jak i EF. Nie trzeba dodawać, że jest to także słaby punkt „surowego” SQL. Aby uzyskać naprawdę dobrą wydajność w zasadzie z dowolnym ORM innym niż NHibernate, musisz wdrożyć własną fasadę buforującą.

    Istnieje również „rozszerzenie” pamięci podręcznej L2 dla EF4, co jest w porządku , ale tak naprawdę nie jest hurtowym zamiennikiem pamięci podręcznej na poziomie aplikacji.

  4. Liczba zapytań

    Relacyjne bazy danych są oparte na zestawach danych. Są naprawdę dobre w tworzeniu dużych ilości danych w krótkim czasie, ale nie są tak dobre pod względem opóźnienia zapytań, ponieważ każde polecenie wiąże się z pewnym obciążeniem. Dobrze zaprojektowana aplikacja powinna wykorzystać mocne strony tego DBMS i spróbować zminimalizować liczbę zapytań i zmaksymalizować ilość danych w każdym z nich.

    Teraz nie mówię, aby przesyłać zapytania do całej bazy danych, gdy potrzebujesz tylko jednego wiersza. Co mówię jest, jeśli potrzebujesz Customer, Address, Phone, CreditCardi Orderwiersze w tym samym czasie w celu odbycia jedną stronę, to należy poprosić o nich wszystkich w tym samym czasie, nie wykonać każde zapytanie osobno. Czasami jest gorzej, zobaczysz kod, który wysyła kwerendę do tego samego Customerrekordu 5 razy z rzędu, najpierw, aby uzyskać Id, a Namenastępnie EmailAddress, a następnie ... to jest absurdalnie nieefektywne.

    Nawet jeśli musisz wykonać kilka zapytań, które działają na całkowicie różnych zestawach danych, zwykle bardziej wydajne jest przesłanie ich do bazy danych jako pojedynczego „skryptu” i zwrócenie wielu zestawów wyników. Niepokoi Cię ogólny koszt, a nie całkowita ilość danych.

    Może to zabrzmieć jak zdrowy rozsądek, ale często bardzo łatwo jest zgubić wszystkie zapytania wykonywane w różnych częściach aplikacji; Twój dostawca członkostwa pyta tabele użytkowników / ról, twoja akcja Nagłówek pyta o koszyk, twoja akcja Menu pyta o tabelę mapy witryny, twoja akcja na pasku bocznym pyta o listę polecanych produktów, a następnie być może twoja strona jest podzielona na kilka odrębnych autonomicznych obszarów, które przeprowadź osobne zapytania do Tabeli Historii zamówień, Ostatnio oglądane, Kategorii i Zapasów, a zanim się zorientujesz, wykonujesz 20 zapytań, zanim zaczniesz obsługiwać stronę. Po prostu całkowicie niszczy wydajność.

    Niektóre frameworki - i myślę tu głównie o NHibernate - są niesamowicie sprytne i pozwalają na użycie czegoś takiego jak futures, które dzielą całe zapytania i próbują wykonać je wszystkie naraz, w ostatniej możliwej chwili. AFAIK, jesteś sam, jeśli chcesz to zrobić za pomocą dowolnej technologii Microsoft; musisz wbudować go w logikę aplikacji.

  5. Indeksowanie, predykaty i prognozy

    Przynajmniej 50% deweloperów, z którymi rozmawiam, a nawet niektórzy DBA wydają się mieć problem z koncepcją obejmowania indeksów. Myślą: „cóż, Customer.Namekolumna jest indeksowana, więc każde wyszukiwanie nazwy powinno być szybkie”. Tyle że to nie działa w ten sposób, chyba że Nameindeks obejmuje konkretną kolumnę, której szukasz. W SQL Server jest to zrobione INCLUDEw CREATE INDEXinstrukcji.

    Jeśli naiwnie używasz SELECT *wszędzie - i to mniej więcej to, co zrobi każdy ORM, chyba że wyraźnie określisz inaczej za pomocą projekcji - wtedy DBMS może bardzo dobrze zignorować twoje indeksy, ponieważ zawierają nieobjęte kolumny. Projekcja oznacza na przykład, że zamiast tego:

    from c in db.Customers where c.Name == "John Doe" select c
    

    Robisz to zamiast tego:

    from c in db.Customers where c.Name == "John Doe"
    select new { c.Id, c.Name }
    

    I będzie to dla większości nowoczesnych ORMs, instruować go tylko iść i kwerendy Idi Namekolumn, które są przypuszczalnie objętych indeksem (ale nie Email, LastActivityDatelub jakikolwiek inny kolumny zdarzyło się trzymać tam).

    Bardzo łatwo jest również całkowicie wyeliminować wszelkie korzyści związane z indeksowaniem przy użyciu nieodpowiednich predykatów. Na przykład:

    from c in db.Customers where c.Name.Contains("Doe")
    

    ... wygląda prawie identycznie jak nasze poprzednie zapytanie, ale w rzeczywistości spowoduje pełne skanowanie tabeli lub indeksu, ponieważ się tłumaczy LIKE '%Doe%'. Podobnie inne zapytanie, które wygląda podejrzanie prosto, to:

    from c in db.Customers where (maxDate == null) || (c.BirthDate >= maxDate)
    

    Zakładając, że masz indeks BirthDate, ten predykat ma dużą szansę, aby uczynić go całkowicie bezużytecznym. Nasz hipotetyczny programista najwyraźniej próbował stworzyć coś w rodzaju dynamicznego zapytania („filtruj datę urodzenia tylko, jeśli określono ten parametr”), ale nie jest to właściwy sposób, aby to zrobić. Zamiast tego napisane w ten sposób:

    from c in db.Customers where c.BirthDate >= (maxDate ?? DateTime.MinValue)
    

    ... teraz silnik DB wie, jak to sparametryzować i przeprowadzić wyszukiwanie indeksu. Jedna niewielka, pozornie nieznaczna zmiana w wyrażeniu zapytania może drastycznie wpłynąć na wydajność.

    Niestety, LINQ ogólnie sprawia, że ​​pisanie złych zapytań jest zbyt łatwe, ponieważ czasami dostawcy są w stanie odgadnąć, co próbowaliście zrobić, i zoptymalizować zapytanie, a czasem nie. W efekcie powstają frustrująco niespójne wyniki, które byłyby oślepiająco oczywiste (w każdym razie dla doświadczonego DBA), gdybyś właśnie napisał zwykły stary SQL.

    Zasadniczo wszystko sprowadza się do tego, że naprawdę musisz uważnie obserwować zarówno wygenerowany SQL, jak i plany wykonania, do których prowadzą, a jeśli nie osiągniesz oczekiwanych rezultatów, nie bój się ominąć Warstwa ORM raz na jakiś czas i ręcznie koduj SQL. Dotyczy to każdej ORM, nie tylko EF.

  6. Transakcje i blokowanie

    Czy potrzebujesz wyświetlać aktualne dane do milisekundy? Może - to zależy - ale prawdopodobnie nie. Niestety, Entity Framework nie dajenolock , możesz używać tylko READ UNCOMMITTEDna poziomie transakcji (nie na poziomie tabeli). W rzeczywistości żaden z ORM nie jest szczególnie wiarygodny w tym zakresie; jeśli chcesz robić brudne odczyty, musisz zejść do poziomu SQL i pisać zapytania ad-hoc lub procedury składowane. Sprowadza się to do tego, jak łatwo jest to zrobić w ramach.

    Entity Framework przeszedł długą drogę w tym względzie - wersja 1 EF (w .NET 3.5) była okropna, sprawiła, że ​​niezwykle trudno było przebić się przez abstrakcję „bytów”, ale teraz masz ExecuteStoreQuery i Tłumacz , więc to naprawdę nieźle. Zaprzyjaźnij się z tymi facetami, ponieważ będziesz ich często używać.

    Istnieje również kwestia blokowania zapisu i zakleszczeń oraz ogólnej praktyki trzymania blokad w bazie danych przez jak najkrótszy czas. Pod tym względem większość ORM (w tym Entity Framework) faktycznie jest lepsza niż surowy SQL, ponieważ zawierają one wzorzec jednostki pracy , którym w EF jest SaveChanges . Innymi słowy, możesz „wstawiać” lub „aktualizować” lub „usuwać” byty w treści swojego serca, kiedy tylko chcesz, mając pewność, że żadne zmiany nie zostaną faktycznie wprowadzone do bazy danych, dopóki nie wykonasz jednostki pracy.

    Należy pamiętać, że UOW nie jest analogiczny do długotrwałej transakcji. UOW nadal korzysta z optymistycznych funkcji współbieżności ORM i śledzi wszystkie zmiany w pamięci . Do ostatniego zatwierdzenia nie jest emitowana ani jedna instrukcja DML. Dzięki temu czasy transakcji są jak najniższe. Jeśli zbudujesz aplikację przy użyciu surowego SQL, osiągnięcie tego odroczonego zachowania jest dość trudne.

    Co to w szczególności oznacza dla EF: spraw, aby twoje jednostki pracy były jak najgrubsze i nie przydzielaj ich, dopóki nie będziesz absolutnie tego potrzebował. Zrób to, a skończysz z znacznie mniejszą rywalizacją o blokadę niż przy użyciu indywidualnych poleceń ADO.NET w przypadkowych momentach.

Podsumowując:

EF jest całkowicie odpowiedni dla aplikacji o dużym natężeniu ruchu / o wysokiej wydajności, podobnie jak każda inna struktura jest odpowiednia do aplikacji o dużym natężeniu ruchu / o wysokiej wydajności. Liczy się sposób korzystania z niego. Oto szybkie porównanie najpopularniejszych frameworków i ich funkcji pod względem wydajności (legenda: N = nieobsługiwane, P = częściowe, Y = tak / obsługiwane):

                                | L2S | EF1 | EF4 | NH3 | ADO
                                +-----+-----+-----+-----+-----
Lazy Loading (entities)         |  N  |  N  |  N  |  Y  |  N
Lazy Loading (relationships)    |  Y  |  Y  |  Y  |  Y  |  N
Eager Loading (global)          |  N  |  N  |  N  |  Y  |  N
Eager Loading (per-session)     |  Y  |  N  |  N  |  Y  |  N
Eager Loading (per-query)       |  N  |  Y  |  Y  |  Y  |  Y
Level 1 (Identity) Cache        |  Y  |  Y  |  Y  |  Y  |  N
Level 2 (Query) Cache           |  N  |  N  |  P  |  Y  |  N
Compiled Queries                |  Y  |  P  |  Y  |  N  | N/A
Multi-Queries                   |  N  |  N  |  N  |  Y  |  Y
Multiple Result Sets            |  Y  |  N  |  P  |  Y  |  Y
Futures                         |  N  |  N  |  N  |  Y  |  N
Explicit Locking (per-table)    |  N  |  N  |  N  |  P  |  Y
Transaction Isolation Level     |  Y  |  Y  |  Y  |  Y  |  Y
Ad-Hoc Queries                  |  Y  |  P  |  Y  |  Y  |  Y
Stored Procedures               |  Y  |  P  |  Y  |  Y  |  Y
Unit of Work                    |  Y  |  Y  |  Y  |  Y  |  N

Jak widać, EF4 (obecna wersja) nie wypada zbyt źle, ale prawdopodobnie nie jest najlepszy, jeśli wydajność jest twoim głównym zmartwieniem. NHibernate jest znacznie bardziej dojrzały w tym obszarze, a nawet Linq to SQL zapewnia pewne funkcje zwiększające wydajność, których EF jeszcze nie ma. Surowe ADO.NET często będzie szybsze w przypadku bardzo specyficznych scenariuszy dostępu do danych, ale po złożeniu wszystkich elementów tak naprawdę nie oferuje wielu ważnych korzyści, które można uzyskać z różnych platform.

I żeby się upewnić, że brzmię jak zepsuty zapis, nic z tego nie ma znaczenia, jeśli nie zaprojektujesz właściwie bazy danych, aplikacji i strategii dostępu do danych. Wszystkie elementy powyższej tabeli służą poprawie wydajności wykraczającej poza poziom podstawowy; przez większość czasu sama podstawa wymaga największej poprawy.


38
Cóż za niesamowita i kompleksowa odpowiedź!

2
+1 (więcej, gdybym mógł) - jedna z najlepszych odpowiedzi, jakie tu widziałem i nauczyłem się kilku rzeczy - dziękuję za podzielenie się tym!
BrokenGlass,

1
To świetna odpowiedź, nawet jeśli nie zgadzam się ze wszystkim, o czym wspomniano. Tabela porównująca ORM nie zawsze jest poprawna. Co to jest leniwe ładowanie jednostek? Masz na myśli leniwie załadowane kolumny? To jest obsługiwane w L2S. Jak myślisz, dlaczego NH nie obsługuje skompilowanych zapytań? Myślę, że nazwane zapytania HQL można wstępnie skompilować. EF4 nie obsługuje wielu zestawów wyników.
Ladislav Mrnka

11
Muszę zdecydowanie nie zgodzić się z niewykwalifikowanym stwierdzeniem „EF jest w porządku w przypadku aplikacji o dużym ruchu / wydajności”, wielokrotnie widzieliśmy, że tak nie jest. To prawda, może nie zgadzamy się na to, co „wysokiej wydajności” oznacza, ale na przykład optymalizacja stron internetowych w dół do 500 ms i posiadające 400ms + stanowi, że spędził w niewytłumaczalny sposób wewnątrz ramy (i tylko 10ms rzeczywiście uderzenie SQL) nie jest „w porządku” w niektórych sytuacjach, jest to absolutnie nie do przyjęcia dla naszego zespołu programistów.
Nick Craver

1
Prosta uwaga na temat kontraktów futures w EF. Nie są one oficjalnie dostarczane przez zespół MS EF, ale można je osiągnąć poprzez projekty stron trzecich, które definiują przyszłe <> rozszerzenia IQueryable <>. Na przykład EntityFramework.Extended przez LoreSoft, dostępny w NuGet. Moje osobiste testy w aplikacjach produkcyjnych wykazują wzrost wydajności nawet 10-krotnie podczas pakowania dziesiątek niezależnych zapytań (wszystkie zapytania mogą być wykonywane równolegle, nikt nie wymaga wyniku poprzedniego) w jednej partii przy użyciu Future. Również AsNoTracking () znacznie poprawia wydajność podczas odczytu wielu rekordów, bez późniejszej aktualizacji.
David Oliván Ubieto

38

Edycja: W oparciu o świetną odpowiedź na @Aaronaught dodaję kilka punktów kierujących na wydajność za pomocą EF. Te nowe punkty są poprzedzone przez Edycja.


Największą poprawę wydajności w witrynach o dużym ruchu uzyskuje się poprzez buforowanie (= przede wszystkim unikanie przetwarzania przez serwer WWW lub zapytania do bazy danych), a następnie przetwarzanie asynchroniczne, aby uniknąć blokowania wątków podczas wykonywania zapytań do bazy danych.

Nie ma żadnej odpowiedzi na pytanie, ponieważ zawsze zależy to od wymagań dotyczących aplikacji i złożoności zapytań. Prawda jest taka, że ​​produktywność programistów z EF ukrywa złożoność, za którą w wielu przypadkach prowadzi się do nieprawidłowego użycia EF i strasznej wydajności. Pomysł, że możesz udostępnić abstrakcyjny interfejs wysokiego poziomu dla dostępu do danych i będzie on płynnie działać we wszystkich przypadkach, nie działa. Nawet z ORM musisz wiedzieć, co dzieje się za abstrakcją i jak prawidłowo z niej korzystać.

Jeśli nie masz wcześniejszego doświadczenia z EF, napotkasz wiele wyzwań związanych z wydajnością. Możesz popełnić znacznie więcej błędów podczas pracy z EF w porównaniu do ADO.NET. Ponadto w EF jest wykonywanych wiele dodatkowych operacji, więc EF zawsze będzie znacznie wolniejszy niż natywny ADO.NET - można to zmierzyć za pomocą prostej aplikacji sprawdzającej koncepcję.

Jeśli chcesz uzyskać najlepszą wydajność z EF, najprawdopodobniej będziesz musiał:

  • Bardzo dokładnie zweryfikuj dostęp do danych za pomocą narzędzia do profilowania SQL i przejrzyj zapytania LINQ, jeśli poprawnie używają Linq-to-podmiotów zamiast Linq-to-objects
  • Bardzo ostrożnie korzystaj z zaawansowanych funkcji optymalizacji EF, takich jak MergeOption.NoTracking
  • W niektórych przypadkach używaj ESQL
  • Prekompilowane zapytania, które są często wykonywane
  • Pomyśl o skorzystaniu z opakowania buforowania EF, aby uzyskać funkcję „buforowania drugiego poziomu” dla niektórych zapytań
  • Użyj widoków SQL lub niestandardowych odwzorowanych zapytań SQL (wymaga ręcznego zarządzania plikiem EDMX) w niektórych scenariuszach dla często używanych prognoz lub agregacji, które wymagają poprawy wydajności
  • Użyj rodzimego SQL i procedur przechowywanych dla niektórych zapytań, które nie zapewniają wystarczającej wydajności, gdy są zdefiniowane w Linq lub ESQL
  • Edycja: Ostrożnie używaj zapytań - każde zapytanie prowadzi osobną podróż do bazy danych. EFv4 nie ma grupowania zapytań, ponieważ nie jest w stanie używać wielu zestawów wyników na jedno polecenie bazy danych. EFv4.5 będzie obsługiwał wiele zestawów wyników dla mapowanych procedur przechowywanych.
  • Edycja: Ostrożnie pracuj z modyfikacjami danych. Znowu EF całkowicie brakuje grupowania poleceń . Tak więc w ADO.NET możesz użyć pojedynczego SqlCommandzawierającego wiele wstawek, aktualizacji lub usunięć, ale z EF każde takie polecenie zostanie wykonane w osobnej rundzie do bazy danych.
  • Edycja: Ostrożnie pracuj z mapą tożsamości / pamięcią podręczną tożsamości. EF ma specjalną metodę ( GetByKeyw ObjectContext API lub FindDbContext API), aby najpierw wykonać zapytanie do pamięci podręcznej. Jeśli użyjesz Linq-to-podmiotów lub ESQL, utworzy on objazd do bazy danych, a następnie zwróci istniejącą instancję z pamięci podręcznej.
  • Edycja: Ostrożnie używaj szybkiego ładowania. Nie zawsze jest to rozwiązanie korzystne dla wszystkich, ponieważ tworzy jeden ogromny zestaw danych . Jak widać, jest to dużo dodatkowej złożoności i o to właśnie chodzi. ORM sprawia, że ​​mapowanie i materializacja są prostsze, ale w przypadku wydajności wydajność będzie znacznie bardziej złożona i będziesz musiał dokonać kompromisów.

Nie jestem pewien, czy SO nadal używa L2S. Opracowali nowy ORM open source o nazwie Dapper i myślę, że głównym celem tego rozwoju było zwiększenie wydajności.


Ladislav, to naprawdę pomocna odpowiedź. Po raz pierwszy słyszę o Dapper (i w konsekwencji odkryłem PetaPoco, Massive) - i wygląda to na ciekawy pomysł.

1
Wydaje się, że SO używa teraz kombinacji LINQ na SQL i Dapper: samsaffron.com/archive/2011/03/30/... Cytat: „Używamy naszego nowego ORM [Dapper] do konkretnego problemu: mapowania sparametryzowanego SQL na obiekty biznesowe Nie używamy go jako pełnowymiarowej ORM. Nie robi relacji ani innych dzwonków i gwizdków. To pozwala nam kontynuować korzystanie z LINQ-2-SQL tam, gdzie wydajność nie ma znaczenia i przenosić wszystkie nasze wbudowane SQL do korzystania z naszego mapera, ponieważ jest szybszy i bardziej elastyczny ”.

5
@Slauma cóż, to jest stwierdzenie sprzed miesięcy, ogólnie wszystkie nowe prace nad SO są wykonywane w Dapper, na przykład nowa tabela, którą dodałem dzisiaj, nawet nie znajduje się w pliku dbml.
Sam Saffron

1
@Sam: Czy jest nowy post na blogu o aktualnej strategii dostępu do danych w SO? Byłoby bardzo interesujące! Czy Dapper został w międzyczasie przedłużony? Zrozumiałem, że Dapper nie jest kompletnym ORM, nie wspiera relacji - a co z aktualizacjami, wstawkami, usunięciami, transakcjami, śledzeniem zmian itp.
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.