Entity Framework Performance-Problem
Habe ich eine interessante performance-Problem mit Entity Framework. Ich bin der Verwendung von Code First.
Hier ist die Struktur meiner Entitäten:
Ein Buch kann viele Bewertungen.
Eine Überprüfung ist im Zusammenhang mit einem einzigen Buch.
Eine Prüfung kann eine oder viele Kommentare.
Ein Kommentar ist im Zusammenhang mit einer Überprüfung.
public class Book
{
public int BookId { get; set; }
//...
public ICollection<Review> Reviews { get; set; }
}
public class Review
{
public int ReviewId { get; set; }
public int BookId { get; set; }
public Book Book { get; set; }
public ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int CommentId { get; set; }
public int ReviewId { get; set; }
public Review Review { get; set; }
}
Ich aufgefüllt, meine Datenbank mit vielen Daten und fügte hinzu, die richtigen Indizes. Ich versuche zum abrufen eines einzelnen Buches, die 10.000 Bewertungen auf es mit dieser Abfrage:
var bookAndReviews = db.Books.Where(b => b.BookId == id)
.Include(b => b.Reviews)
.FirstOrDefault();
Diesem besonderen Buch hat 10.000 Bewertungen. Die Leistung dieser Abfrage ist rund 4 Sekunden. Läuft die genau die gleiche Abfrage (mit Hilfe von SQL-Profiler) tatsächlich zurück in keine Zeit an allen. Ich die gleiche Abfrage und ein SqlDataAdapter-und benutzerdefinierte Objekte, um die Daten abzurufen, und es geschieht in unter 500 Millisekunden.
Mit ANTS Performance Profiler sieht es aus wie ein Großteil der Zeit wird damit verbracht, ein paar andere Dinge:
Die Equals-Methode aufgerufen wird, ist 50 Millionen mal.
Weiß jemand, warum es nennen müsste, diese 50 Millionen mal und wie konnte ich erhöhen Sie die Leistung für diese?
- Hast du eigentlich sehen, welche Abfrage wird erzeugt durch deine Aussage oder sind Sie der Annahme, dass es die optimale Abfrage?
- Geben EF-Profiler versuchen.
- Das problem ist nicht die Abfrage, wie ich festgestellt habe. Ich nahm mir die genaue Abfrage, EF generiert und verwendet es in einer Sql-Daten-Adapter mit regelmäßigen ADO.net laden die gleichen Objekte manuell. Läuft es in weniger als einer Sekunde.
- Was ist der IL Aussehen?
- Ihre navigation sollte in den Eigenschaften markiert werden
virtual
. Ich bin mir nicht sicher, ob das im Zusammenhang oder nicht. - Nur wenn Sie möchten, lazy loading, was ich nicht.
- So...
This particular book has 10,000 reviews.
und Sie nicht wollen, lazy loading. Sie sehen nicht, dass da ein problem? Nicht zu vergessen, Ihren Zirkelbezug inBook
undReview
. - Nein. Ich will nicht faul laden. Ich möchte eifrig laden alle 10.000 Datensätze, so kann ich bestimmen, ob wir können dies in unserer Anwendung. Wir haben Szenarien, in denen wir müssen die Last extrem großes Objekt-Graphen. Ich sehe nicht, wie machen es träge geladen vs eifrig möchte diese verbessern.
- Versucht EF-Profiler. Was ich sagen kann, es zeigt auch die Abfrage ausführen schnell, aber gerade gibt mir eine Warnung, dass "eine große Anzahl von Objekten zurückgegeben werden." Es tut mir nicht sagen, warum Gleich aufgerufen wird, so viele Male, und wie kann ich vermeiden, dass.
- Sehr hilfreich für Fragen wie diese Überlegungen zur Leistung von Entity Framework 4, 5 und 6. msdn.microsoft.com/en-jm/data/hh949853.aspx#2
Du musst angemeldet sein, um einen Kommentar abzugeben.
Es klingt schon Recht verdächtig. Sie haben 10.000 Bewertungen und 50.000.000 Aufrufe
Equals
. Nehme an, dass dies verursacht wird, die von Identitäts-Karte, intern realisiert durch EF. Identität Karte sorgt dafür, dass jede Person mit eindeutigem Schlüssel wird verfolgt von dem Zusammenhang, nur mal so, wenn der Kontext bereits die Instanz mit dem Schlüssel geladenen Datensatz aus der Datenbank, es wird nicht materialisieren neue Instanz und verwendet stattdessen die vorhandenen. Nun, wie kann das übereinstimmen mit diesen zahlen? Meine erschreckende Vermutung:Das bedeutet, dass jeder neue Datensatz ist im Vergleich mit jedem vorhandenen Datensatz in der identity map. Durch die Anwendung der Mathematik zu berechnen, wird die Summe aller Vergleich können wir etwas namens "Arithmetic sequence":
Ich hoffe ich habe keine Fehler in meinen Annahmen oder Berechnungen. Warten! Ich hoffe, ich habe Fehler gemacht, denn dieser scheint nicht gut.
Deaktivieren Sie die änderungsnachverfolgung = hoffentlich ausschalten Identität Karte überprüfen.
Kann es schwierig sein. Beginnen Sie mit:
Aber es ist eine große chance, dass Ihre navigation Eigenschaft nicht aufgefüllt werden (denn es wird durch die änderungsnachverfolgung). In diesem Fall verwenden Sie diese Methode:
Jedenfalls können Sie sehen, welche den Objekt-Typ übergeben wird, Entspricht? Ich denke, es sollte vergleichen, nur primären Schlüssel und sogar 50M integer-Vergleiche sollte nicht so ein problem.
Als eine Randnotiz EF ist langsam - es ist bekannte Tatsache. Es verwendet auch die Reflexion intern beim materialisieren Entitäten, so einfach 10.000 Datensätze mit "einige Zeit". Es sei denn, Sie haben das bereits getan, können Sie auch schalten Sie die dynamische proxy-Erstellung (
db.Configuration.ProxyCreationEnabled
).AsNoTracking
verkürzt die Zeit für die Materialisierung zu 50%. Ich könnte mir vorstellen, wenn das snapshot-Erstellung für Personen geladen, die als verfolgte teurer ist als der AufrufEquals
in der identity map. Wenn Sie rufen dieselbe Abfrage ein zweites mal (als verfolgt) in den gleichen Kontext gibt es schnell (weniger als 1/10 von dem ersten Anruf), viel schneller als das laden ohne tracking - das lässt mich vermuten, dass dieEquals
überprüfen Sie in der identity map ist relativ Billig.Include
funktioniert auch mitAsNoTracking()
die navigation Sammlung wird aufgefüllt. (Oder meinst du, dass das reverse-navigation-EigenschaftReview.Book
nicht aufgefüllt werden?)Ich weiß, das klingt lahm, aber haben Sie versucht, die andere Weise herum, z.B.:
Habe ich bemerkt, manchmal bessere Leistung von EF, wenn Sie nähern sich Ihre Fragen auf diese Weise (aber ich hatte noch nicht die Zeit, um herauszufinden, warum).