Entity Framework: Performance

01 August 2014
Andreas Heizmann

Erstellt man Datenbankabfragen mit LINQ to Entities über das Entity Framework kommt man früher oder später in die Situation, dass die geforderte Performance bei manchen Anfragen ausbleibt.

Falls Sie gute Testdaten haben, stellen Sie das nicht erst im Produktivbetrieb fest und können somit das Kind noch vorm Ertrinken im Brunnen bewahren. Im Internet sowie Literatur finden Sie sehr schnell jede Menge von Optimierungsvorschlägen, Tipps & Tricks sowie allerlei Mythen zum Thema Performance + Entity Framework. Mit diesem Blogeintrag möchte ich meine eigenen Erfahrungen dazu mit Ihnen teilen. Um Ihre Erwartungen jetzt etwas auszubremsen nehme ich das Fazit schon mal vorweg: „Es gibt kein Allheilmittel“. Aber das haben Sie sicher auch schon irgendwo gelesen.

Ich werde in diesem Beitrag nicht auf alle Dos und Don’ts eingehen die im Netz kursieren, sondern mich auf einige Wesentliche beschränken.

Vorbereitung:

Bevor Sie eine langsame Anfrage optimieren können, müssen Sie diese erst einmal aufspüren. Das gelingt in vielen Fällen direkt schon beim Debuggen in Visual Studio, wenn Ihnen beim Zeile für Zeile Durchlauf plötzlich das System einfriert oder zumindest kurze Aussetzer auftreten.

Eleganter lassen sich solche Stellen natürlich z.B. mir den eingebauten Profiling Tools (Instrumentation Test) von Visual Studio aufspüren.

Nun, da man die Anfrage isoliert hat, kann man beginnen und zunächst einmal den Ist-Zustand erfassen. CPU Last, reservierter Arbeitsspeicher sowie Netzwerkauslastung über die Zeit sollten vorerst ausreichen. Die einfachste Möglichkeit bietet dazu die eingebaute „Ressourcen und Leistungsüberwachung“ von Windows. (Ausführen: perfmon.)

Optional können Sie auch mit Visual Studio Performance Reports erfassen. Diese haben unter anderem den Vorteil, dass man sie direkt Vergleichen kann. Einziger Nachteil: Die Reports werden schnell relativ groß und können gerne mal mehrere Gigabytes auf Ihrer Festplatte einnehmen. Man kann diese zwar auch als serialisierte Version speichern (.vsps), verliert damit jedoch die Möglichkeit die Daten zeitlich zu filtern.

Analyse:

Die Performance einer Anfrage wird durch viele Faktoren beeinflusst. Bevor Sie Optimieren, sollten Sie herausfinden welcher Faktor den meisten Einfluss auf die Performance hat. Die wichtigsten im Überblick:

  1. Die Menge an Datensätzen die abgerufen wird.

Häufig werden unbeabsichtigt zu viele Daten abgerufen. Das passiert vor allem dann, wenn man mit IEnumerable<T> Collections arbeitet.

Ein kleines Beispiel dazu:

Hier werden zunächst alle Einträge abgerufen und danach im Speicher gezählt.

  1. IEnumerable<DB.Objekt>iEnumerableQuery=dbContext.Objekt;
  2. varcount=iEnumerableQuery.Count();

Bei Verwendung von IQueryable<T> wird das Zählen vom SQL Server übernommen:

  1. IQueryable<DB.Objekt>iQueryableQuery=dbContext.Objekt;
  2. varcount2=iQueryableQuery.Count();

In vielen Fällen muss man jedoch zwingend alle oder zumindest viele Datensätze abrufen um z.B. komplexe Berechnungen durchzuführen. Dabei sollten Sie möglichst vermeiden direkt eine Liste des Entity Typs abzurufen:

  1. List<DB.Objekt>objektList=dbContext.Objekt.ToList();

Die Abfrage wird länger dauern und erheblich mehr Arbeitsspeicher belegen. Erstellen Sie stattdessen lieber ein neues eigenes Objekt:

  1. List<CachedObjekt>cachedObjectList=dbContext.Objekt.Select(x=>newCachedObjekt()
  2. {
  3. ID=x.ID,
  4. ...
  5. }).ToList();

Noch schneller, dafür vielleicht nicht so stilvoll lassen sich die Daten ohne Entity Framework, klassisch mit dem SQLDataReader abrufen. Mehrere Abfragen sollten parallel ablaufen.

  1. Die Anfragen sind komplex

Bei komplexen, tief verschachtelten Anfragen erstellt das Entity Framework gerne mal SQL mit 1000 Zeilen und mehr. Falls diese dann längere Abfragezeiten haben kann das folgende Ursachen haben:

-          Das Umwandeln in SQL dauert lange.

-          Der SQL Server benötigt für das Erstellen des Ausführungsplans mehr Zeit.

Hier kann es sinnvoll sein Anfragen parallel auszuführen oder komplexe Anfragen in kleinere aufzuteilen.

Es gibt auch die Möglichkeit Anfragen als Compiled Query zu erstellen. Damit wird das Umwandeln in SQL beschleunigt. Ab Entity Framework Version 5 werden diese Compiled Queries automatisch erstellt, gespeichert und bei gleichen Abfragen erneut verwendet.

Auf der Seite des SQL Servers gibt es auch Möglichkeiten zur Optimierung. Manchmal hilft schon eine schnellere oder zusätzliche Festplatte oder das Aufteilen von großen Tabellen in mehrere Partitionen. Infos und Blogeinträge gibt es dazu mehr als genug im Netz, leider lösen die in der Regel immer nur die Performance Probleme des jeweiligen Autors.

  1. Alte vs. neue Technologien

Ein Sprung auf die nächste Version kann sich bei manchen Problemen lohnen. Egal ob nun Entity Framework oder SQL Server. Implementiert bzw. installiert sind diese in der Regel schnell. Machen Sie sich jetzt aber bitte nicht zu große Hoffnungen. Bei meinen Tests haben sich Technologiesprünge nie wirklich bemerkbar auf die Performance ausgewirkt.

Sowohl Menge der Datensätze wie auch die Größe des erzeugten SQL können Sie sich im SQL Server Profiler anzeigen lassen. Mit dem kostenpflichtig Tool: Entity Framework Profiler 2.0 von Hibernating Rhinosfunktioniert dies ebenfalls sehr gut und übersichtlich.

Das Fazit habe ich ja vorweg schon gegeben. Nun hoffe ich, dass Ihnen zumindest der ein oder andere Tipp weiterhelfen kann.