Sonntag, März 14, 2010

Applikationsperformance

PPPuuuhhh, die Woche wurden größere Probleme mit der Performance festgestellt. Das Problem war so Akut, dass dringend gehandelt werden musste.

Um die Performance einer Anwendung zu verbessern muss man immer wieder messen, was wie lange braucht. Bei den Problemen, die bei uns aufgetreten sind, sind alle Schichten/Anwendungskomponenten involviert, so dass eine Fehlersuche nicht das einfachste ist. Simple Messung der Zeiten an bestimmten Punkten ist meistens schon genug. Ein ganzes Stück schicker geht es mit einem Profiler. Beim Messen musste ich feststellen, dass es überall nicht optimal läuft. Beim Client dauerte das Aufbereiten der Eingaben schon ewig, im Service brauchte das Speichern viel Zeit bei der Übergabe an die DB und noch schlimmer war die Verarbeitung in der DB.

Die Verarbeitungszeit im Service war nicht so tragisch, allerdings auch störend. Uns war bereits bei der Realisierung bewusst, dass das Speichern mit Linq to Sql bei Massendaten nicht optimal läuft, trotzdem wir schon einige Optimierungen genutzt haben. Der letzte Optmierungsschritt für die L2S-Verarbeitung war die Übergabe an die DB vorbei durchzuführen. Im letzten Jahr hatten wir in einem anderen Projekt bereits ähnliche Optimierung durchgeführt, allerdings habe ich diesmal einen anderen Weg, vermutlich effektiveren Weg gewählt, die Daten werden mittels Table-Valued Parametern übergeben, dadurch is eine einfache Übergabe der Massendaten möglich. Ein super Einführung in das Thema mit Beispielen für die .Net-Realisierung gibt es unter http://www.sommarskog.se/arrays-in-sql-2008.html. Letztendlich muss man einen eigenen Type im Sql-Server erstellen, optional eine Stored Procedure anlegen, bei mir enthält sie Insert/Update/Delete und anschließend im .NET-Code eine DataTable mit den Feldern des SqlServer Types anlegen und den Parameter konfigurieren. Beim Parameter muss der TypeName gesetzt werden, sowie der SqlDbType auf Structure.

Diese Optimierung entspannte die Lösung schon um einiges, aber weiterhin festzustellen war, dauerte die Ausführung in der DB immer noch viel zu lange. Bei aktivierten Triggern in der DB dauerte die Verarbeitung mehrere Minuten, ohne Trigger 10s. Für das Problem haben wir bisher noch keine Lösung, dies ist ein akuter Eingriff in das Verarbeitungsverhalten unserer DB. Ich bin überhaupt kein Freund von Triggern und bin auch der Meinung, dass Trigger versteckte DB-Logik sind. Vermutlich muss die gesamte Trigger-Verarbeitung herausgelöst werden.

Im Client war der Code nicht optimal, allerdings nun auch nicht so ineffektiv, allerdings für den Fall traten 20000 Aufrufe auf einen Code-Block auf, der Xml-Verarbeitung durchführte. 20000-Aufrufe in 3-4min, ungefähr 100 Aufrufe pro Sekunde, sind zu langsam und zum Teil für den Benutzer nur schwer nachzuvollziehen. Das Ergebnis wurde auch durch die Trial-Version von RedGates ANTS Profiler bestätigt. Die Optimierungen durchzuführen vielen mir relativ schwierig, so dass ich einen Kollegen gebeten habe, einfach den Code mal mit mir durchzusehen und Anmerkungen abzugeben. Die Fragen und Anregen waren sehr effektiv, so dass die Geschwindigkeit verdreifacht wurde. Eigentlich waren es ganz einfache Sachen, versuchen Type-Konvertierungen zu vermeiden, die 2. Xml-Verarbeitung reduzieren. Eine andere kleinere Optimierung war die Anpassung des XPaths, statt “//” (decendant-or-self) in der Abfrage zu verwenden, wird nun der XPath voll angegeben. Des Weiteren habe ich das Konvertierungsergebnis bei Type-Konvertierungen gespeichert und für eine erneute Konvertierung gespeichert, ob das sich als Vorteil im regulären Betrieb erweist, muss sich noch zeigen. Mit allen Optimierungen ist der Client bis 4 mal schneller, allerdings vermutlich nur unter Entwicklerbedingungen (wenige unterschiedliche Werte). Allerdings bringt auch das Release-Compile durch Inlining evtl. noch einige kleinere Vorteile.

Die Optimierung in Service und Client sind extrem wichtig und sollen die Nutzbarkeit deutlich verbessern, allerdings ohne eine Anpassung in unserer Datenbank, werden die Ergebnisse nichts bringen, da unsere Trigger noch immer zu viel Zeit verschlingen. Also noch einmal Ideen sammeln, wie wir das Problem reduzieren können.

Keine Kommentare: