Ist das Entity Framework für stark frequentierte Websites geeignet?

176

Ist Entity Framework 4 eine gute Lösung für eine öffentliche Website mit potenziell 1000 Treffern / Sekunde?

Meines Erachtens ist EF eine praktikable Lösung für meist kleinere oder Intranet-Websites, lässt sich jedoch nicht ohne Weiteres für so etwas wie eine beliebte Community-Website skalieren (ich weiß, dass SO LINQ to SQL verwendet, aber ich hätte gerne mehr Beispiele / Beweise). ..)

Jetzt stehe ich am Scheideweg zwischen einem reinen ADO.NET-Ansatz und EF4. Denken Sie, dass die verbesserte Entwicklerproduktivität mit EF die verlorene Leistung und den granularen Zugriff von ADO.NET (mit gespeicherten Prozeduren) wert ist? Gibt es ernsthafte Probleme, mit denen eine Website mit hohem Datenverkehr konfrontiert sein könnte? Wurde EF verwendet?

Danke im Voraus.

Niaher
quelle
1
Sie verstehen die Skalierung nicht. Skalieren bedeutet 10-facher Durchsatz, wenn Sie 10-fache Kapazität hinzufügen. Warum sollte EF dies verhindern? Es fügt jeder Datenbankauslastung einen konstanten Faktor-Overhead hinzu.
USR

Antworten:

152

Es hängt ein wenig davon ab, wie viel Abstraktion Sie benötigen . Alles ist ein Kompromiss; zum Beispiel, EF und NHibernate einführen große Flexibilität , die Daten in interessanten und exotischen Modellen für die Darstellung - aber als Folge sie tun Aufwand hinzufügen. Auffälliger Overhead.

Wenn Sie nicht brauchen , um zwischen Datenbankanbieter wechseln kann, und andere pro-Client - Tabellenlayout, und wenn Sie Ihre Daten in erster Linie sind zu lesen , und wenn Sie nicht brauchen , das gleiche Modell in EF verwenden zu können, SSRS , ADO.NET Data Services, usw. - Wenn Sie die absolute Leistung als Schlüsselmaßstab verwenden möchten, können Sie weitaus schlechtere Ergebnisse erzielen als mit dapper . In unseren Tests, die sowohl auf LINQ-to-SQL als auch auf EF basieren, stellen wir fest, dass EF in Bezug auf die Raw- Leseleistung erheblich langsamer ist, vermutlich aufgrund der Abstraktionsschichten (zwischen Speichermodell usw.) und der Materialisierung.

Hier bei SO sind wir besessen-zwanghaft in Bezug auf die rohe Leistung und freuen uns, den Entwicklungshit zu tragen, etwas Abstraktion zu verlieren, um an Geschwindigkeit zu gewinnen. Daher ist unser primäres Tool zum Abfragen der Datenbank " dapper" . Dies ermöglicht es uns sogar, unser bereits vorhandenes LINQ-to-SQL-Modell zu verwenden, aber einfach: Es ist viel schneller. In Leistungstests entspricht dies im Wesentlichen genau der Leistung des manuellen Schreibens des gesamten ADO.NET-Codes (Parameter, Datenlesegeräte usw.), jedoch ohne das Risiko, dass ein Spaltenname falsch ist. Es ist jedoch SQL-basiert (obwohl es gerne SPROCs verwendet, wenn dies das Gift Ihrer Wahl ist). Dies hat den Vorteil, dass keine zusätzliche Verarbeitung erforderlich ist , sondern ein System für Leute, die SQL mögen. Was ich denke: keine schlechte Sache!

Eine typische Abfrage könnte beispielsweise sein:

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

Das ist praktisch, spritzensicher usw. - aber ohne jede Menge Datenlesegeräte. Beachten Sie, dass es zwar sowohl horizontale als auch vertikale Partitionen zum Laden komplexer Strukturen verarbeiten kann, jedoch kein verzögertes Laden unterstützt (aber: Wir sind große Fans des expliziten Ladens - weniger Überraschungen).

In dieser Antwort möchte ich nicht erwähnen, dass EF nicht für großvolumige Arbeiten geeignet ist. einfach: ich weiß, dass dapper es drauf ankommt.

Marc Gravell
quelle
25
+1 für adrette. Die Verwendung eines komplexen ORM für Lesemodelle ist nur unnötig. Der Ansatz, den wir jetzt verfolgen, besteht darin, ein ORM für unser Domain-Modell (wobei das ausgefallene ORM-Zeug tatsächlich nützlich ist) und ein passendes für unser Lesemodell zu verwenden. Dies ermöglicht superschnelle Anwendungen.
2
@Marc, danke für die tolle Antwort - ich kann meine Entscheidung endlich mit Zuversicht treffen! Werde auf jeden Fall später genauer auf dapper eingehen. Wirklich wie es ist nur eine Datei :)
3
Ich habe meinen eigenen ORM geschrieben. Es ist langsam. Ich sah schick aus und mochte es. Jetzt benutze ich dapper für all meine Reads und mein eigenes ORM für Inserts (das FK, Transaktionen und all die guten Sachen unterstützt). Es ist der am einfachsten lesbare Code, den ich je geschrieben habe.
2
@ acidzombie24 dapper unterstützt Transaktionen, und der Contrib-Teil von dapper (nicht Teil der Nuget-Bereitstellung) erhält Einfügeoptionen usw. Nur der Vollständigkeit halber erwähnt. Ich bin froh, dass Dapper praktisch war.
Marc Gravell
1
Ich habe noch nie einen Videokurs zu einem Thema gemacht. Es gibt einige Videos da draußen, aber nicht von mir. Ich neige dazu, ein geschriebenes Wort zu sein
Marc Gravell
217

Die Frage "Welches ORM soll ich verwenden?" Zielt wirklich auf die Spitze eines riesigen Eisbergs ab, wenn es um die allgemeine Datenzugriffsstrategie und die Leistungsoptimierung in einer groß angelegten Anwendung geht.

Alle folgenden Dinge ( ungefähr in der Reihenfolge ihrer Wichtigkeit) werden den Durchsatz beeinflussen, und alle werden von den meisten wichtigen ORM-Frameworks (manchmal auf unterschiedliche Weise) behandelt:

  1. Datenbankdesign und -pflege

    Dies ist mit großem Abstand die wichtigste Determinante für den Durchsatz einer datengesteuerten Anwendung oder Website und wird von Programmierern häufig völlig ignoriert.

    Wenn Sie nicht die richtigen Normalisierungstechniken verwenden, ist Ihre Website zum Scheitern verurteilt. Wenn Sie keine Primärschlüssel haben, ist fast jede Abfrage hundeschwach. Wenn Sie bekannte Anti-Patterns verwenden, z. B. Tabellen für Schlüssel-Wert-Paare (AKA Entity-Attribute-Value), explodieren Sie die Anzahl der physischen Lese- und Schreibvorgänge.

    Wenn Sie die Funktionen, die Ihnen die Datenbank bietet, wie Seitenkomprimierung, FILESTREAMSpeicher (für Binärdaten), SPARSESpalten, hierarchyidHierarchien usw. (alle SQL Server-Beispiele) nicht nutzen, werden Sie nicht in der Nähe von sehen Leistung, die Sie sehen könnten .

    Sie sollten sich über Ihre Datenzugriffsstrategie Gedanken machen, nachdem Sie Ihre Datenbank entworfen und sich davon überzeugt haben, dass sie zumindest vorerst so gut wie möglich ist.

  2. Eifriges vs. faules Laden

    Die meisten ORMs verwendeten eine Technik, die als verzögertes Laden von Beziehungen bezeichnet wird. Dies bedeutet, dass standardmäßig jeweils eine Entität (Tabellenzeile) geladen wird und jedes Mal, wenn eine oder mehrere verwandte (fremde) Entitäten geladen werden müssen, ein Roundtrip zur Datenbank durchgeführt wird Schlüssel) Zeilen.

    Dies ist keine gute oder schlechte Sache, sondern hängt vielmehr davon ab, was mit den Daten tatsächlich gemacht wird und wie viel Sie im Vorfeld wissen. Manchmal ist Lazy-Loading genau das Richtige. Beispielsweise kann NHibernate entscheiden, überhaupt nichts abzufragen und einfach einen Proxy für eine bestimmte ID zu generieren . Wenn Sie nur die ID selbst benötigen, warum sollten Sie danach fragen? Wenn Sie dagegen versuchen, einen Baum jedes einzelnen Elements in einer dreistufigen Hierarchie zu drucken, wird das verzögerte Laden zu einer O (N²) -Operation, die für die Leistung äußerst schlecht ist.

    Ein interessanter Vorteil der Verwendung von "reinem SQL" (dh ADO.NET-Abfragen / gespeicherten Prozeduren) besteht darin, dass Sie gezwungen sind, genau zu überlegen, welche Daten für die Anzeige eines bestimmten Bildschirms oder einer bestimmten Seite erforderlich sind. ORMs und Funktionen zum verzögerten Laden hindern Sie nicht daran , aber sie bieten Ihnen die Möglichkeit, ... faul zu sein und die Anzahl der von Ihnen ausgeführten Abfragen aus Versehen zu explodieren. Sie müssen also die Funktionen Ihres ORM verstehen, die Sie benötigen, und die Anzahl der Abfragen, die Sie für eine bestimmte Seitenanforderung an den Server senden, stets im Auge behalten.

  3. Caching

    Alle wichtigen ORMs verfügen über einen Cache der ersten Ebene, den "Identitätscache" der AKA. Wenn Sie also dieselbe Entität zweimal anhand ihrer ID anfordern, ist kein zweiter Roundtrip erforderlich ) gibt Ihnen die Möglichkeit, optimistische Parallelität zu verwenden.

    Der L1-Cache ist in L2S und EF ziemlich undurchsichtig. Man muss sich darauf verlassen, dass er funktioniert. NHibernate geht expliziter vor ( Get/ Loadvs. Query/ QueryOver). Solange Sie versuchen, nach ID zu fragen, sollten Sie hier in Ordnung sein. Viele Leute vergessen den L1-Cache und suchen immer wieder nach derselben Entität, die nicht ihrer ID entspricht (dh nach einem Suchfeld). Wenn Sie dies tun müssen, sollten Sie die ID oder sogar die gesamte Entität für zukünftige Suchvorgänge speichern.

    Es gibt auch einen Level 2 Cache ("Query Cache"). NHibernate hat dies eingebaut. Linq to SQL und Entity Framework haben Abfragen kompiliert , wodurch die Auslastung des Anwendungsservers erheblich reduziert werden kann, indem der Abfrageausdruck selbst kompiliert wird, die Daten jedoch nicht zwischengespeichert werden. Microsoft scheint dies eher als Anwendungsproblem als als als Problem des Datenzugriffs zu betrachten, und dies ist eine große Schwachstelle von L2S und EF. Unnötig zu erwähnen, dass dies auch eine Schwachstelle von "rohem" SQL ist. Um mit einem anderen ORM als NHibernate eine wirklich gute Leistung zu erzielen, müssen Sie Ihre eigene Caching-Fassade implementieren.

    Es gibt auch eine L2-Cache- "Erweiterung" für EF4, die in Ordnung ist , aber keinen wirklichen Ersatz für einen Cache auf Anwendungsebene darstellt.

  4. Anzahl der Abfragen

    Relationale Datenbanken basieren auf Datensätzen . Sie sind wirklich gut auf der Herstellung große Datenmengen in kürzester Zeit, aber sie sind bei weitem nicht so gut in Bezug auf Abfrage Latenz , weil es eine bestimmte Menge an Overhead in jedem Befehl beteiligt. Eine gut gestaltete App sollte die Stärken dieses DBMS nutzen und versuchen, die Anzahl der Abfragen zu minimieren und die Datenmenge in jedem zu maximieren.

    Jetzt sage ich nicht, die gesamte Datenbank abzufragen, wenn Sie nur eine Zeile benötigen. Was ich sagen will ist, wenn Sie die Notwendigkeit Customer, Address, Phone, CreditCard, und OrderReihen alle zur gleichen Zeit , um eine einzelne Seite zu dienen, dann sollten Sie fragen , für sie alle zur gleichen Zeit, nicht ausführen jeweils getrennt Abfrage. Manchmal ist es schlimmer als das. Sie werden sehen, dass Code Customer5 Mal hintereinander denselben Datensatz abfragt , um zuerst den Id, dann den Name, dann den EmailAddress, dann ... zu erhalten. Es ist lächerlich ineffizient.

    Selbst wenn Sie mehrere Abfragen ausführen müssen, die alle mit völlig unterschiedlichen Datensätzen arbeiten, ist es in der Regel immer noch effizienter, alle Abfragen als einzelnes "Skript" an die Datenbank zu senden und mehrere Ergebnismengen zurückzugeben. Es ist der Aufwand, um den Sie sich kümmern, nicht die Gesamtmenge der Daten.

    Das mag sich nach gesundem Menschenverstand anhören, aber es ist oft sehr einfach, den Überblick über alle Abfragen zu verlieren, die in verschiedenen Teilen der Anwendung ausgeführt werden. Ihr Mitgliedschaftsanbieter fragt die Benutzer- / Rollentabellen ab, Ihre Header-Aktion fragt den Einkaufswagen ab, Ihre Menü-Aktion fragt die Site-Map-Tabelle ab, Ihre Sidebar-Aktion fragt die vorgestellte Produktliste ab und dann ist Ihre Seite möglicherweise in einige separate autonome Bereiche unterteilt, die Fragen Sie die Tabellen "Bestellverlauf", "Zuletzt angezeigt", "Kategorie" und "Inventar" separat ab. Bevor Sie dies wissen, führen Sie 20 Abfragen aus, bevor Sie überhaupt mit der Bereitstellung der Seite beginnen können. Es zerstört einfach die Leistung.

    Einige Frameworks - und ich denke hier hauptsächlich an NHibernate - sind unglaublich schlau und ermöglichen es Ihnen, so genannte Futures zu verwenden, die ganze Abfragen stapeln und versuchen, sie alle auf einmal in der letzten Minute auszuführen. AFAIK, Sie sind auf sich allein gestellt, wenn Sie dies mit einer der Microsoft-Technologien tun möchten. Sie müssen es in Ihre Anwendungslogik integrieren.

  5. Indizierung, Prädikate und Projektionen

    Mindestens 50% der Entwickler, mit denen ich spreche, und sogar einige Datenbankadministratoren scheinen Probleme mit dem Konzept der Indexabdeckung zu haben. Sie denken, "nun, die Customer.NameSpalte ist indiziert, also sollte jede Suche, die ich nach dem Namen mache, schnell sein." Dies funktioniert jedoch nur, wenn der NameIndex die bestimmte Spalte abdeckt, nach der Sie suchen. In SQL Server ist dies INCLUDEin der CREATE INDEXAnweisung erledigt .

    Wenn Sie SELECT *überall naiv verwenden - und dies ist mehr oder weniger das, was jeder ORM tun wird, sofern Sie nicht ausdrücklich etwas anderes mit einer Projektion angeben -, kann das DBMS Ihre Indizes möglicherweise vollständig ignorieren, da sie nicht abgedeckte Spalten enthalten. Eine Projektion bedeutet zum Beispiel, dass stattdessen:

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

    Sie tun dies stattdessen:

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

    Und dies wird für die meisten modernen ORMs, weisen sie nur zu gehen und fragen Sie die Idund NameSpalten , die vermutlich durch den Index abgedeckt sind (aber nicht das Email, LastActivityDateoder was auch immer andere Spalten , die Sie dort bleiben passiert).

    Es ist auch sehr einfach, Indexvorteile durch die Verwendung unangemessener Prädikate vollständig zunichte zu machen. Zum Beispiel:

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

    ... sieht fast identisch mit unserer vorherigen Abfrage aus, führt jedoch zu einem vollständigen Tabellen- oder Index-Scan, da er in übersetzt wird LIKE '%Doe%'. Ähnlich ist eine andere Abfrage, die verdächtig einfach aussieht:

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

    Vorausgesetzt, Sie haben einen Index BirthDate, hat dieses Prädikat eine gute Chance, ihn völlig unbrauchbar zu machen. Unser hypothetischer Programmierer hier hat offensichtlich versucht, eine Art dynamische Abfrage zu erstellen ("filtern Sie das Geburtsdatum nur, wenn dieser Parameter angegeben wurde"), aber dies ist nicht der richtige Weg, dies zu tun. Stattdessen so geschrieben:

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

    ... jetzt kann die DB-Engine dies parametrieren und eine Indexsuche durchführen. Eine geringfügige, scheinbar unbedeutende Änderung des Abfrageausdrucks kann die Leistung drastisch beeinträchtigen.

    Leider macht es LINQ im Allgemeinen allzu einfach, schlechte Abfragen wie diese zu schreiben, da die Anbieter manchmal raten können, was Sie versucht haben, und die Abfrage optimieren können, und manchmal nicht. Am Ende stehen Ihnen frustrierend inkonsistente Ergebnisse zur Verfügung, die für einen erfahrenen Datenbankadministrator (jedenfalls) offensichtlich gewesen wären, wenn Sie nur einfaches altes SQL geschrieben hätten.

    Grundsätzlich kommt es darauf an, dass Sie sowohl das generierte SQL als auch die Ausführungspläne, zu denen es führt, genau im Auge behalten müssen. Wenn Sie nicht die erwarteten Ergebnisse erzielen, haben Sie keine Angst, das zu umgehen Von Zeit zu Zeit eine ORM-Schicht erstellen und die SQL von Hand codieren. Dies gilt für jedes ORM, nicht nur für EF.

  6. Transaktionen und Sperren

    Müssen Sie Daten anzeigen, die bis zur Millisekunde aktuell sind? Vielleicht - es kommt darauf an - aber wahrscheinlich nicht. Leider bietet Ihnen Entity Framework keine Funktionennolock , die Sie nur READ UNCOMMITTEDauf Transaktionsebene (nicht auf Tabellenebene) verwenden können. Tatsächlich ist keiner der ORMs in dieser Hinsicht besonders zuverlässig. Wenn Sie Dirty Reads durchführen möchten, müssen Sie sich auf die SQL-Ebene begeben und Ad-hoc-Abfragen oder gespeicherte Prozeduren schreiben. Es kommt also darauf an, wie einfach es für Sie ist, dies im Rahmen zu tun.

    Entity Framework hat in dieser Hinsicht einen langen Weg zurückgelegt - Version 1 von EF (in .NET 3.5) war fürchterlich, und es war unglaublich schwierig, die "Entities" -Abstraktion zu durchbrechen, aber jetzt haben Sie ExecuteStoreQuery und Translate , also ist es wirklich nicht so schlecht. Schließe Freundschaften mit diesen Jungs, weil du sie häufig verwendest.

    Es gibt auch das Problem der Schreibsperren und Deadlocks sowie die allgemeine Praxis, Sperren in der Datenbank so kurz wie möglich zu halten. In dieser Hinsicht sind die meisten ORMs (einschließlich Entity Framework) in der Regel besser als unformatiertes SQL, da sie die Einheit des Arbeitsmusters kapseln , das in EF SaveChanges ist . Mit anderen Worten, Sie können Entitäten nach Herzenslust "einfügen" oder "aktualisieren" oder "löschen". Dabei können Sie sicher sein, dass keine Änderungen tatsächlich in die Datenbank übertragen werden, bis Sie die Arbeitseinheit festschreiben.

    Beachten Sie, dass eine UOW nicht mit einer lang laufenden Transaktion vergleichbar ist. Die UOW verwendet weiterhin die optimistischen Parallelitätsfunktionen des ORM und verfolgt alle Änderungen im Speicher . Bis zum endgültigen Festschreiben wird keine einzige DML-Anweisung ausgegeben. Dies hält die Transaktionszeiten so gering wie möglich. Wenn Sie Ihre Anwendung mit Raw SQL erstellen, ist es ziemlich schwierig, dieses verzögerte Verhalten zu erreichen.

    Was dies konkret für EF bedeutet: Machen Sie Ihre Arbeitseinheiten so grob wie möglich und legen Sie sie erst fest, wenn Sie es unbedingt müssen. Wenn Sie dies tun, kommt es zu einer viel geringeren Sperrenkonkurrenz als bei der zufälligen Verwendung einzelner ADO.NET-Befehle.

Abschließend:

EF ist für Anwendungen mit hohem Datenverkehr und hoher Leistung vollkommen in Ordnung, genau wie jedes andere Framework für Anwendungen mit hohem Datenverkehr und hoher Leistung. Was zählt, ist, wie Sie es verwenden. Hier ist ein kurzer Vergleich der beliebtesten Frameworks und ihrer Leistungsmerkmale (Legende: N = Nicht unterstützt, P = Teilweise, Y = Ja / Unterstützt):

                                | 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

Wie Sie sehen, schneidet EF4 (die aktuelle Version) nicht allzu schlecht ab, aber es ist wahrscheinlich nicht die beste, wenn die Leistung Ihr Hauptanliegen ist. NHibernate ist in diesem Bereich viel ausgereifter und sogar Linq to SQL bietet einige leistungssteigernde Funktionen, die EF noch nicht bietet. RAW ADO.NET ist für sehr spezielle Datenzugriffsszenarien häufig schneller , aber wenn Sie alle Komponenten zusammenfassen, bietet es nicht wirklich viele wichtige Vorteile, die Sie aus den verschiedenen Frameworks ziehen.

Und um ganz sicherzugehen, dass ich wie ein kaputter Rekord klinge, spielt dies keine Rolle, wenn Sie Ihre Datenbank-, Anwendungs- und Datenzugriffsstrategien nicht richtig entwerfen. Alle Elemente in der obigen Tabelle dienen dazu, die Leistung über die Grundlinie hinaus zu verbessern . Meistens ist die Basislinie selbst diejenige, die am meisten verbessert werden muss.

Aaronaught
quelle
38
Was für eine großartige und umfassende Antwort!
2
+1 (mehr, wenn ich könnte) - eine der besten Antworten, die ich seit einiger Zeit hier gesehen habe, und ich habe ein oder zwei Dinge gelernt - danke, dass du das geteilt hast!
BrokenGlass
1
Dies ist eine großartige Antwort, auch wenn ich nicht mit allem einverstanden bin, was erwähnt wurde. Die Tabelle zum Vergleichen von ORMs ist nicht immer korrekt. Was ist Entities Lazy Loading? Meinen Sie faul geladene Spalten? Das wird in L2S unterstützt. Warum unterstützt NH Ihrer Meinung nach keine kompilierten Abfragen? Ich denke, dass benannte HQL-Abfragen vorkompiliert werden können. EF4 unterstützt nicht mehrere Ergebnismengen.
Ladislav Mrnka
11
Ich muss stark mit dem uneingeschränkten nicht einverstanden „EF ist völlig in Ordnung für stark frequentierte / High-Performance - Anwendungen“ statement, haben wir immer wieder gesehen , dass dies nicht der Fall ist. Zugegeben, vielleicht sind wir uns nicht einig darüber, was "hohe Leistung" bedeutet, aber zum Beispiel, wenn Webseiten auf 500 ms optimiert werden und 400 ms + davon unerklärlicherweise im Framework verbracht werden (und nur 10 ms tatsächlich auf SQL zutreffen ), ist dies für einige Situationen nicht "in Ordnung". Für unser Entwicklerteam ist das absolut inakzeptabel.
Nick Craver
1
Einfache Notiz über Futures in EF. Sie werden nicht offiziell vom MS EF-Team bereitgestellt, können jedoch durch Projekte von Drittanbietern erreicht werden, in denen zukünftige <> Erweiterungen für IQueryable <> definiert sind. Zum Beispiel EntityFramework.Extended von LoreSoft, verfügbar in NuGet. Meine persönlichen Tests in Produktionsanwendungen zeigen einen bis zu 10-fachen Leistungszuwachs, wenn Dutzende von nicht abhängigen Abfragen (alle Abfragen können parallel ausgeführt werden, niemand benötigt das Ergebnis einer vorherigen Abfrage) in einem einzigen Stapel mit Future gepackt werden. Auch AsNoTracking () verbessert die Leistung erheblich, wenn nur viele Datensätze gelesen werden, und führt keine spätere Aktualisierung durch.
David Oliván Ubieto
38

Bearbeiten: Basierend auf der großartigen Antwort von @Aaronaught füge ich ein paar Punkte hinzu, die auf die Leistung mit EF abzielen. Diesen neuen Punkten wird das Präfix Bearbeiten vorangestellt.


Die größte Verbesserung der Leistung bei Websites mit hohem Datenaufkommen wird durch Caching (= Vermeiden von Webserververarbeitung oder Datenbankabfragen) und anschließende asynchrone Verarbeitung erzielt, um das Blockieren von Threads bei Datenbankabfragen zu vermeiden.

Es gibt keine kugelsichere Antwort auf Ihre Frage, da dies immer von den Anwendungsanforderungen und der Komplexität der Anfragen abhängt. Die Wahrheit ist, dass die Entwicklerproduktivität mit EF die Komplexität verbirgt, die in vielen Fällen zu einer falschen Verwendung von EF und einer schrecklichen Leistung führt. Die Idee, dass Sie eine abstrahierte Oberfläche auf hoher Ebene für den Datenzugriff verfügbar machen können und dass sie in allen Fällen reibungslos funktioniert, funktioniert nicht. Auch bei ORM müssen Sie wissen, was sich hinter der Abstraktion verbirgt und wie Sie sie richtig einsetzen.

Wenn Sie noch keine Erfahrung mit EF haben, werden Sie beim Umgang mit Leistung vielen Herausforderungen begegnen. Im Vergleich zu ADO.NET können Sie bei der Arbeit mit EF viel mehr Fehler machen. Außerdem wird in EF eine Menge zusätzlicher Verarbeitungsschritte ausgeführt, sodass EF immer erheblich langsamer als natives ADO.NET ist. Dies können Sie anhand einer einfachen Proof-of-Concept-Anwendung messen.

Wenn Sie die beste Leistung von EF erzielen möchten, müssen Sie höchstwahrscheinlich:

  • Überarbeiten Sie Ihren Datenzugriff mit SQL Profiler sehr sorgfältig und überprüfen Sie Ihre LINQ-Abfragen, ob sie Linq-to-Entities anstelle von Linq-to-Objects verwenden
  • Verwenden Sie erweiterte EF-Optimierungsfunktionen wie MergeOption.NoTracking
  • Verwenden Sie in einigen Fällen ESQL
  • Vorkompilieren Sie häufig ausgeführte Abfragen
  • Nutzen Sie den EF Caching-Wrapper, um bei einigen Abfragen eine Cache-ähnliche Funktion der zweiten Ebene zu erhalten
  • Verwenden Sie in einigen Szenarien für häufig verwendete Projektionen oder Aggregationen SQL-Ansichten oder benutzerdefiniert zugeordnete SQL-Abfragen (manuelle Verwaltung der EDMX-Datei erforderlich), die Leistungsverbesserungen erfordern
  • Verwenden Sie systemeigenes SQL und gespeicherte Prozeduren für einige Abfragen, die bei der Definition in Linq oder ESQL keine ausreichende Leistung bieten
  • Bearbeiten: Verwenden Sie Abfragen sorgfältig - jede Abfrage führt einen separaten Roundtrip zur Datenbank durch. EFv4 bietet keine Stapelverarbeitung von Abfragen, da es nicht möglich ist, mehrere Ergebnismengen pro ausgeführtem Datenbankbefehl zu verwenden. EFv4.5 unterstützt mehrere Ergebnismengen für zugeordnete gespeicherte Prozeduren.
  • Bearbeiten: Sorgfältig mit Datenänderungen arbeiten. Auch hier fehlt EF die Befehls-Stapelverarbeitung vollständig . In ADO.NET können Sie also einzelne SqlCommandBefehle verwenden, die mehrere Einfügungen, Aktualisierungen oder Löschvorgänge enthalten. Mit EF wird jedoch jeder dieser Befehle in einem separaten Roundtrip zur Datenbank ausgeführt.
  • Bearbeiten: Sorgfältig mit der Identitätskarte / dem Identitätscache arbeiten. EF verfügt über eine spezielle Methode ( GetByKeyin der ObjectContext-API oder Findin der DbContext-API), um zuerst den Cache abzufragen. Wenn Sie Linq-to-Entities oder ESQL verwenden, wird ein Roundtrip zur Datenbank erstellt und anschließend eine vorhandene Instanz aus dem Cache zurückgegeben.
  • Bearbeiten: Verwenden Sie sorgfältig eifriges Laden. Es ist nicht immer eine Win-Win-Lösung, da ein einziger großer Datensatz erstellt wird . Wie Sie sehen, ist es eine Menge zusätzlicher Komplexität und das ist der springende Punkt. ORM vereinfacht das Mapping und die Materialisierung, aber wenn Sie sich mit der Leistung befassen, wird dies viel komplexer und Sie müssen Kompromisse eingehen.

Ich bin nicht sicher, ob SO noch L2S verwendet. Sie entwickelten neues Open Source ORM namens Dapper und ich denke, der Hauptgrund für diese Entwicklung war die Leistungssteigerung.

Ladislav Mrnka
quelle
Ladislav, das ist eine wirklich hilfreiche Antwort. Dies ist das erste Mal, dass ich von Dapper höre (und infolgedessen PetaPoco, Massive, entdeckte) - und es sieht nach einer interessanten Idee aus.
1
SO scheint jetzt eine Mischung aus LINQ to SQL und Dapper zu verwenden: samsaffron.com/archive/2011/03/30/… Quote: "Wir verwenden unser neues ORM [Dapper] für ein bestimmtes Problem: das Zuordnen von parametrisiertem SQL zu Geschäftsobjekten Wir verwenden es nicht als vollwertiges ORM. Es erledigt keine Beziehungen und andere Dinge. Dies ermöglicht es uns, LINQ-2-SQL weiterhin zu verwenden, bei denen die Leistung keine Rolle spielt, und alle unsere Inline-SQL-Befehle zu portieren, um unseren Mapper zu verwenden. denn es ist schneller und flexibler. "
5
@Slauma gut, das ist eine Aussage von vor Monaten, im Allgemeinen werden alle neuen Arbeiten an SO in Dapper durchgeführt, zum Beispiel ist eine neue Tabelle, die ich heute hinzugefügt habe, nicht einmal in der dbml-Datei.
Sam Saffron
1
@Sam: Gibt es einen neuen Blogbeitrag über die aktuelle Datenzugriffsstrategie auf SO? Wäre sehr interessant! Wurde Dapper inzwischen erweitert? Mein Verständnis war, dass Dapper kein vollständiger ORM ist, keine Unterstützung für Beziehungen - und was ist mit Updates, Einfügungen, Löschungen, Transaktionen, Änderungsnachverfolgung usw.