Das Entity Framework ist zu langsam. Was sind meine Optionen? [geschlossen]


93

Ich habe das Mantra "Nicht vorzeitig optimieren" befolgt und meinen WCF-Service mit Entity Framework codiert.

Ich habe jedoch die Leistung profiliert und das Entity Framework ist zu langsam. (Meine App verarbeitet 2 Nachrichten in ca. 1,2 Sekunden, wobei die (Legacy-) App, die ich neu schreibe, 5-6 Nachrichten gleichzeitig ausführt. (Die Legacy-App ruft Sprocs für ihren DB-Zugriff auf.)

Meine Profilerstellung zeigt, dass Entity Framework den größten Teil der Zeit pro Nachricht benötigt.

Also, was sind meine Optionen?

  • Gibt es da draußen bessere ORMs?
    (Etwas, das nur das normale Lesen und Schreiben von Objekten unterstützt und es schnell macht ..)

  • Gibt es eine Möglichkeit, Entity Framework schneller zu machen?
    ( Hinweis : Wenn ich schneller sage, meine ich auf lange Sicht nicht den ersten Anruf. (Der erste Anruf ist langsam (15 Sekunden für eine Nachricht), aber das ist kein Problem. Ich brauche ihn nur, um für den Rest schnell zu sein der Nachrichten.)

  • Eine mysteriöse dritte Option, die mir hilft, mehr Geschwindigkeit aus meinem Dienst herauszuholen.

HINWEIS: Die meisten meiner DB-Interaktionen sind Erstellen und Aktualisieren. Ich wähle sehr wenig aus und lösche.


Das klingt wie eine Wiederholung von 'linq is slow'. Woher weißt du, dass es EF ist? Haben Sie alle Ihre Änderungen profiliert?
Maess

6
Einige der Antworten verweisen auf die Fragen. Nach meiner Erfahrung hat die Langsamkeit in EF wenig mit den Abfragen zu tun, sondern mit den Materialisierungskosten. Diese Kosten hängen häufig mit der Änderungsverfolgung und den Auswirkungen auf die erstellten Instanzen zusammen. Leider habe ich keine Silberkugel für Sie, daher ist dies nur ein Kommentar, aber ich würde empfehlen, zu prüfen, ob die Profilerstellung hohe Materialisierungskosten ergibt, und in diesem Fall zu untersuchen, was gegen diese Kosten getan werden kann.
Anthony Pegram

@Maess - Ich dachte, ich hätte angegeben, dass ich ein Profil erstellt habe, und festgestellt, dass EF / DB langsam war. So oder so, ja, ich habe es getan. Ich habe es profiliert und es sind EF / DB-Interaktionen, die den Hauptschuldigen ausmachen.
Vaccano

@Anthony - Wird Materialisierung nicht zuerst ausgeführt? Wenn ja, haben Sie Recht, dass es sehr langsam ist. Der erste Lauf ist super langsam. Aber wie gesagt, ich mache mir darüber keine allzu großen Sorgen. Das Problem ist der Gesamtdurchsatz. (Wenn das nicht Materialisierung ist, muss ich einige Nachforschungen
anstellen, um festzustellen,

1
@Vaccano, nein, Materialisierung ist der Prozess des Entnehmens der Daten aus der Datenbank und des Instanziierens und Auffüllens des Diagramms von Objekten, um diese Daten darzustellen. Ich spreche nicht von der Leistung beim ersten Ausführen, wenn der Code angepaßt wird (oder wenn SQL Server möglicherweise den Abfrageausführungsplan erstellt), sondern davon, was jedes Mal passiert, wenn Sie Daten in Form von Objekten erhalten.
Anthony Pegram

Antworten:


46

Beginnen Sie mit der Profilerstellung der SQL-Befehle, die tatsächlich vom Entity Framework ausgegeben werden. Abhängig von Ihrer Konfiguration (POCO, Self-Tracking-Entitäten) gibt es viel Raum für Optimierungen. Mit der ObjectSet<T>.ToTraceString()Methode können Sie die SQL-Befehle debuggen (die sich nicht zwischen Debug- und Release-Modus unterscheiden sollten) . Wenn Sie auf eine Abfrage stoßen, die einer weiteren Optimierung bedarf, können Sie einige Projektionen verwenden, um EF weitere Informationen darüber zu geben, was Sie erreichen möchten.

Beispiel:

Product product = db.Products.SingleOrDefault(p => p.Id == 10);
// executes SELECT * FROM Products WHERE Id = 10

ProductDto dto = new ProductDto();
foreach (Category category in product.Categories)
// executes SELECT * FROM Categories WHERE ProductId = 10
{
    dto.Categories.Add(new CategoryDto { Name = category.Name });
}

Könnte ersetzt werden durch:

var query = from p in db.Products
            where p.Id == 10
            select new
            {
                p.Name,
                Categories = from c in p.Categories select c.Name
            };
ProductDto dto = new ProductDto();
foreach (var categoryName in query.Single().Categories)
// Executes SELECT p.Id, c.Name FROM Products as p, Categories as c WHERE p.Id = 10 AND p.Id = c.ProductId
{
    dto.Categories.Add(new CategoryDto { Name = categoryName });
}

Ich habe das nur aus meinem Kopf getippt, so dass es nicht genau so ausgeführt wird, aber EF führt tatsächlich einige nette Optimierungen durch, wenn Sie ihm alles erzählen, was Sie über die Abfrage wissen (in diesem Fall benötigen wir die Kategorie-). Namen). Dies ist jedoch nicht mit einem eifrigen Laden (db.Products.Include ("Categories")) vergleichbar, da Projektionen die zu ladende Datenmenge weiter reduzieren können.


39
Diese Antwort klingt vernünftig, bis Sie feststellen, dass anonyme Typen außerhalb der Methode, in der sie definiert sind, nicht verfügbar sind. Wenn Sie ein komplexes Objekt laden und kein Megamoth schreiben möchten, müssen Sie Ihre neuen anonymen Typen in eine Art POCO deserialisieren. Auch dies klingt fast vernünftig, bis Sie feststellen, dass Sie damit im Wesentlichen Ihren eigenen Entity-Rahmen neu geschrieben haben. Welches ist Bullshit.
Doug

5
Dies führte für mich zu einer 15- bis 20-fachen Geschwindigkeitssteigerung.
Dave Cousineau

11
Interessante und hilfreiche Antwort, die noch einige Zeit später gültig ist. @Doug: Das ist kein wirklicher Schwachsinn, da Sie nur die wenigen Abfragen optimieren (mithilfe von Projektionen), bei denen Sie den zusätzlichen Vorteil wirklich benötigen. EF und POCO geben Ihnen vernünftige Standardeinstellungen, was sehr schön ist!
Victor

2
@Doug Die meisten Anwendungen haben Ansichtsmodelle für Nur-Ansicht-Szenarien, richtig? Könnte genauso viel von der Zuordnung machen, wie Sie die Daten herausziehen.
Casey

4
Ich habe immer das Gefühl, dass ORMs die Zukunft sind. Sie machten einfach Sinn, bis ich anfing, sie zu benutzen. Dann habe ich Dapper gefunden . Wenn ich jetzt solche Lösungen sehe, erschrecke ich, wie schnell die Komplexität zunimmt. Das Schreiben von abstrahiertem SQL in C # ist kein Weg durch das Leben.
Michael Silver

80

Tatsache ist, dass Produkte wie Entity Framework IMMER langsam und ineffizient sind, da sie viel mehr Code ausführen.

Ich finde es auch albern, dass Leute vorschlagen, LINQ-Abfragen zu optimieren, die generierte SQL zu betrachten, Debugger zu verwenden, vorkompilieren, viele zusätzliche Schritte unternehmen usw., dh viel Zeit verschwenden. Niemand sagt - Vereinfachen! Jeder möchte die Dinge weiter verkomplizieren, indem er noch mehr Schritte unternimmt (Zeitverschwendung).

Ein vernünftiger Ansatz wäre, EF oder LINQ überhaupt nicht zu verwenden. Verwenden Sie einfaches SQL. Daran ist nichts auszusetzen. Nur weil die Programmierer eine Herdenmentalität haben und sie den Drang verspüren, jedes einzelne neue Produkt zu verwenden, heißt das nicht, dass es gut ist oder funktionieren wird. Die meisten Programmierer denken, wenn sie jeden neuen Code einbauen, der von einem großen Unternehmen veröffentlicht wird, werden sie zu einem intelligenteren Programmierer. überhaupt nicht wahr. Bei der intelligenten Programmierung geht es hauptsächlich darum, mit weniger Kopfschmerzen, Unsicherheiten und in kürzester Zeit mehr zu erreichen. Behalte die Zeit im Kopf! Das ist das wichtigste Element. Versuchen Sie also, Wege zu finden, um es nicht mit der Lösung von Problemen in schlechtem / aufgeblähtem Code zu verschwenden, der einfach geschrieben wurde, um einigen seltsamen sogenannten "Mustern" zu entsprechen.

Entspannen Sie sich, genießen Sie das Leben, machen Sie eine Pause vom Codieren und verwenden Sie keine zusätzlichen Funktionen, Codes, Produkte oder Muster mehr. Das Leben ist kurz und das Leben Ihres Codes ist noch kürzer, und es ist sicherlich keine Raketenwissenschaft. Entfernen Sie Ebenen wie LINQ, EF und andere, und Ihr Code wird effizient ausgeführt, skaliert und ist dennoch leicht zu warten. Zu viel Abstraktion ist ein schlechtes "Muster".

Und das ist die Lösung für Ihr Problem.


155
Dies wirft das Baby mit dem Badewasser raus. Wenn Sie Engpässe optimieren, ist es dumm, EF wegzuwerfen, weil es an einigen Stellen zu langsam ist und an den meisten anderen schnell genug. Warum nicht beide verwenden? EF verarbeitet gespeicherte Prozeduren und unformatiertes SQL einwandfrei. Ich habe gerade eine LINQ-zu-SQL-Abfrage, die mehr als 10 Sekunden dauerte, in einen SP konvertiert, der ~ 1 Sekunde dauert, aber ich werde nicht alle LINQ-zu-SQL-Abfragen rauswerfen. In anderen einfacheren Fällen wurde viel Zeit gespart, da weniger Code und weniger Fehler auftreten. Die Abfragen werden vom Compiler überprüft und stimmen mit der Datenbank überein. Weniger Code ist einfacher zu warten und weniger Platz für Fehler.
JulianR

11
Insgesamt ist Ihr Rat zwar gut, aber ich denke nicht, dass es richtig ist, EF oder andere Abstraktionen aufzugeben, da sie in 10% der Fälle nicht gut funktionieren.
JulianR

49
Plain SQL = einfach zu warten? Nur nicht wahr für sehr große Apps mit viel Geschäftslogik. Das Schreiben von komplexem wiederverwendbarem SQL ist nicht einfach. Persönlich hatte ich einige Leistungsprobleme mit EF, aber diese Probleme sind einfach nicht mit den Vorteilen eines richtigen ORM in Bezug auf ein RAD zu vergleichen und die Dinge trocken zu halten (wenn es um Komplexität geht).
MemeDeveloper

13
+ 10 ^ 100 Zu viel Abstraktion ist ein schlechtes 'Muster'
Makach

57
-1. "EF wird IMMER langsam und ineffizient sein." Ich verstehe nicht, warum Sie so etwas für die absolute Wahrheit halten würden. Wenn mehr Ebenen durchlaufen werden müssen, wird etwas langsamer, aber ob dieser Unterschied überhaupt bemerkbar ist, hängt vollständig von der Situation wie der Datenmenge und der Art der ausgeführten Abfrage ab. Für mich ist dies das Gleiche wie "C # wird IMMER langsam und ineffizient sein", da es eine höhere Abstraktion als C ++ ist. Viele Menschen entscheiden sich jedoch für die Verwendung, da die Produktivitätsgewinne den Leistungsverlust (falls vorhanden) bei weitem ausgleichen. Gleiches gilt für EF
Despertar

37

Ein Vorschlag besteht darin, LINQ to Entity Framework nur für CRUD-Anweisungen mit einem Datensatz zu verwenden.

Schreiben Sie für umfangreichere Abfragen, Suchen, Berichte usw. eine gespeicherte Prozedur und fügen Sie sie dem Entity Framework-Modell hinzu, wie in MSDN beschrieben .

Dies ist der Ansatz, den ich bei einigen meiner Websites gewählt habe, und es scheint ein guter Kompromiss zwischen Produktivität und Leistung zu sein. Entity Framework generiert nicht immer das effizienteste SQL für die jeweilige Aufgabe. Und anstatt die Zeit damit zu verbringen, herauszufinden, warum, spart mir das Schreiben einer gespeicherten Prozedur für die komplexeren Abfragen tatsächlich Zeit. Sobald Sie mit dem Prozess vertraut sind, ist es nicht allzu mühsam, Ihrem EF-Modell gespeicherte Prozesse hinzuzufügen. Und natürlich ist der Vorteil des Hinzufügens zu Ihrem Modell, dass Sie all die stark typisierte Güte erhalten, die durch die Verwendung eines ORM entsteht.


Haben Sie eine Vorstellung von den im Gerüst verwendeten Methoden wie db.athlete.find (id) usw. Wie verhalten sie sich im Vergleich zu ADO.NET oder dapper?
Es ist eine Falle

15

Wenn Sie nur Daten abrufen, ist dies eine große Hilfe für die Leistung, wenn Sie EF anweisen, die von ihm abgerufenen Entitäten nicht im Auge zu behalten. Verwenden Sie dazu MergeOption.NoTracking. EF generiert lediglich die Abfrage, führt sie aus und deserialisiert die Ergebnisse für Objekte, versucht jedoch nicht, Entitätsänderungen oder ähnliches zu verfolgen. Wenn eine Abfrage einfach ist (nicht viel Zeit darauf wartet, dass die Datenbank zurückgegeben wird), kann die Einstellung auf NoTracking die Abfrageleistung verdoppeln.

Siehe diesen MSDN-Artikel in der MergeOption-Enumeration:

Identitätsauflösung, Statusverwaltung und Änderungsverfolgung

Dies scheint ein guter Artikel über die EF-Leistung zu sein:

Leistung und das Entity Framework


9
Bevor jemand dies tut, ist es möglicherweise eine gute Idee, hier zu lesen. stackoverflow.com/questions/9259480/…
leen3o

6

Sie sagen, dass Sie die Anwendung profiliert haben. Haben Sie auch das ORM profiliert? Es gibt einen EF-Profiler von Ayende, der hervorhebt, wo Sie Ihren EF-Code optimieren können. Sie finden es hier:

http://efprof.com/

Denken Sie daran, dass Sie neben Ihrem ORM einen herkömmlichen SQL-Ansatz verwenden können, wenn Sie die Leistung steigern möchten.

Gibt es ein schnelleres / besseres ORM? Abhängig von Ihrem Objekt / Datenmodell können Sie eines der Mikro-ORMs wie Dapper , Massive oder PetaPoco verwenden .

Auf der Dapper-Website werden einige vergleichende Benchmarks veröffentlicht, die Ihnen eine Vorstellung davon geben, wie sie mit anderen ORMs verglichen werden. Es ist jedoch erwähnenswert, dass die Mikro-ORMs den umfangreichen Funktionsumfang der vollständigen ORMs wie EF und NH nicht unterstützen.

Vielleicht möchten Sie einen Blick auf RavenDB werfen . Dies ist eine nicht relationale Datenbank (wieder von Ayende), mit der Sie POCOs direkt speichern können, ohne dass eine Zuordnung erforderlich ist . RavenDB ist für Lesevorgänge optimiert und erleichtert den Entwicklern das Leben erheblich, da keine Manipulationen mehr erforderlich sind und Ihre Objekte diesem Schema zugeordnet werden müssen. Beachten Sie jedoch, dass dies ein erheblich anderer Ansatz zur Verwendung eines ORM-Ansatzes ist und diese auf der Produktwebsite beschrieben werden .


3

Ich habe die Antwort von @Slauma hier sehr nützlich gefunden, um die Dinge zu beschleunigen. Ich habe sowohl für Einfügungen als auch für Aktualisierungen das gleiche Muster verwendet - und die Leistung ist rasant gestiegen.


2

Meiner Erfahrung nach liegt das Problem nicht bei EF, sondern beim ORM-Ansatz selbst.

Im Allgemeinen leiden alle ORMs unter N + 1- Problemen, nicht optimierten Abfragen usw. Meine beste Vermutung wäre, Abfragen aufzuspüren, die zu Leistungseinbußen führen, und zu versuchen, das ORM-Tool zu optimieren oder diese Teile mit SPROC neu zu schreiben.


1
Die Leute erzählen mir das immer wieder. Aber ich werde eine einfache select-Anweisung mit ADO der alten Schule einrichten, und dieselbe einfache select-Anweisung mit einem EF-Kontext und EF ist immer erheblich langsamer. Ich möchte EF wirklich mögen, aber es macht das Leben immer schwieriger anstatt einfacher.
Sinaesthetic

1
@Sinaesthetic Natürlich ist es langsamer. Aus dem gleichen Grund ist Code, der mit Linq to Objects geschrieben wird, normalerweise langsamer als Code, der ohne Linq geschrieben wurde. Die Frage ist nicht wirklich, ob es schneller oder so schnell ist (wie könnte es sein, wenn es unter der Haube noch die von Ihnen ausgegebene Abfrage von Hand ausgeben muss?), Sondern ob 1) es immer noch schnell genug für Ihre Anforderungen ist 2) es spart Sie schreiben den Code 3) Die Vorteile gleichen die Kosten aus. Aufgrund dieser Punkte denke ich, dass EF für viele Projekte geeignet ist.
Casey

@Sinaesthetic Ich möchte auch hinzufügen, dass, wenn Sie kein ORM verwenden, häufig nicht jede SQL-Abfrage genau abgestimmt und optimiert wird, sondern dass die Anwendung letztendlich eine interne, organische und schlechte Entwicklung entwickelt -unterstütztes ORM mit schlechter Leistung, es sei denn, Ihr Team ist außergewöhnlich diszipliniert und sehr besorgt über die Leistung.
Casey


1

Ich bin auch auf dieses Problem gestoßen. Ich hasse es, auf EF zu werfen, weil es so gut funktioniert, aber es ist nur langsam. In den meisten Fällen möchte ich nur einen Datensatz finden oder aktualisieren / einfügen. Selbst einfache Operationen wie diese sind langsam. Ich habe 1100 Datensätze von einer Tabelle in eine Liste zurückgezogen, und dieser Vorgang dauerte mit EF 6 Sekunden. Für mich ist das zu lang, selbst das Speichern dauert zu lange.

Am Ende habe ich mein eigenes ORM gemacht. Ich habe die gleichen 1100 Datensätze aus einer Datenbank abgerufen und mein ORM dauerte 2 Sekunden, viel schneller als EF. Alles mit meinem ORM ist fast augenblicklich. Die einzige Einschränkung im Moment ist, dass es nur mit MS SQL Server funktioniert, aber es könnte geändert werden, um mit anderen wie Oracle zu arbeiten. Ich benutze momentan MS SQL Server für alles.

Wenn Sie mein ORM ausprobieren möchten, finden Sie hier den Link und die Website:

https://github.com/jdemeuse1204/OR-M-Data-Entities

Oder wenn Sie Nugget verwenden möchten:

PM> Install-Package OR-M_DataEntities

Dort ist auch die Dokumentation


0

Eine Optimierung ist erst nach dem Profilieren sinnvoll. Wenn Sie feststellen, dass der DB-Zugriff langsam ist, können Sie auf gespeicherte Prozeduren umstellen und EF beibehalten. Wenn Sie feststellen, dass der EF selbst langsam ist, müssen Sie möglicherweise zu einem anderen ORM wechseln oder überhaupt keinen ORM verwenden.


0

Wir haben eine ähnliche Anwendung (Wcf -> EF -> Datenbank), die problemlos 120 Anfragen pro Sekunde ausführt. Daher bin ich mir mehr als sicher, dass EF hier nicht Ihr Problem ist. Allerdings habe ich bei kompilierten Abfragen erhebliche Leistungsverbesserungen festgestellt.


98% meines Codes sind Anrufe erstellen und aktualisieren. Ich weiß nicht, ob das einen Unterschied macht, aber es ist viel langsamer als 120 pro Sekunde.
Vaccano

Ja, das wäre keine typische Anwendung. Ich würde vorschlagen, dass Sie Ihre Anwendung profilieren. für uns liest es meistens ...
np-hard

0

Ich habe EF, LINQ to SQL und dapper verwendet. Dapper ist der schnellste. Beispiel: Ich brauchte 1000 Hauptdatensätze mit jeweils 4 Unterdatensätzen. Ich habe LINQ für SQL verwendet, es dauerte ungefähr 6 Sekunden. Ich wechselte dann zu dapper, holte 2 Datensatzgruppen aus der einzelnen gespeicherten Prozedur und fügte für jeden Datensatz die Unterdatensätze hinzu. Gesamtzeit 1 Sekunde.

Auch die gespeicherte Prozedur verwendet Tabellenwertfunktionen mit Kreuz anwenden, ich fand Skalarwertfunktionen sehr langsam.

Mein Rat wäre, EF oder LINQ to SQL zu verwenden und in bestimmten Situationen auf dapper umzuschalten.


-1

Das Entity Framework sollte selbst keine größeren Engpässe verursachen. Es besteht die Möglichkeit, dass es andere Ursachen gibt. Sie könnten versuchen, EF auf Linq2SQL umzustellen, beide haben Vergleichsfunktionen und der Code sollte leicht zu konvertieren sein, aber in vielen Fällen ist Linq2SQL schneller als EF.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.