Speed SQL

Optimieren von Datenbankaufrufen mit Profilerstellung für die Ebeneninteraktion

Mark Friedman

Zahlreiche Anwendungen sind explizit auf die Verwendung mehrerer Ebenen ausgerichtet, wobei die Leistung von Aufrufen an die Datenzugriffsebene entscheidend für die allgemeine Reaktionsfähigkeit der Anwendung ist. Die Verwendung mehrerer Ebenen erhöht die Flexibilität der Anwendung. Der Ansatz mit n Ebenen fördert auch die Isolation der Hauptkomponenten, wodurch sowohl Zuverlässigkeit als auch Skalierbarkeit verbessert werden können. Die Verwendung mehrerer Ebenen begünstigt die Skalierbarkeit, da in unterschiedlichen Ebenen isolierte Komponenten dann besser auf verfügbare Computerressourcen verteilt werden können.

Die Profilerstellung für die Ebeneninteraktion (Tier Interaction Profiling, TIP) wurde entwickelt, damit Sie die Leistung der Datenschicht, auf der die Anwendung beruht, erfassen können. TIP ist eine neue Funktion der Visual Studio-Profilerstellungstools, die die Dauer der Verzögerungen der Datenschicht misst, die für .NET Framework-Anwendungen auftritt, wenn diese auf die Ausführung synchroner Aufrufe von ADO.NET-kompatiblen Datenbanken warten. Bei reaktionszeitorientierten Anwendungen, die häufige Datenbankaufrufe ausführen, können Sie mit TIP erfassen, welche Datenanfragen maßgeblich zu den Antwortverzögerungen beitragen.

In diesem Artikel lernen Sie TIP und die zugehörigen Berichterstellungsfunktionen kennen. Auch wird die Instrumentationstechnologie beschrieben, auf der TIP beruht. Und es werden einige bewährte Methoden für die effiziente Verwendung von TIP zur Diagnose von Leistungsproblemen in Verbindung mit der Datenbankaktivität bereitgestellt. Es wird die Verwendung von TIP in einer ASP.NET-Beispielwebanwendung mit zwei Ebenen beschrieben, die datenintensiv ist und mit der LINQ to SQL-Technologie auf Daten aus Microsoft SQL Server zugreift. Schließlich wird erläutert, wie Sie mit den standardmäßigen Leistungstools des SQL-Administrators weitere TIP-Leistungsdaten erfassen können, um einen noch besseren Überblick über die Leistung einer Datenebene zu erhalten.

Erste Schritte mit TIP

TIP fügt dynamisch Instrumentationscode hinzu, um die Dauer von Aufrufen an die Datenebene Ihrer Anwendung während einer Profilerstellungslauf zu messen. Die Visual Studio-Profilerstellungstools stehen in Visual Studio 2010 Premium Edition und Visual Studio 2010 Ultimate Edition zur Verfügung.

Eine Profilerstellungssitzung können Sie starten, indem Sie Leistungs-Assistenten starten im Menü Analysieren oder Leistungsanalyse starten im Menü Debuggen auswählen oder die Tastenkombination ALT+F2 verwenden. Auf der ersten Seite des Leistungs-Assistenten werden Sie zur Auswahl einer Profilerstellungsmethode aufgefordert.

TIP kann mit allen Profilerstellungsmethoden (Sampling, Instrumentation, Speicher oder Parallelität) verwendet werden, ist aber nicht standardmäßig aktiviert. Um TIP zu aktivieren, müssen Sie das Kontrollkästchen Launch profiling after the wizard finishes (Profilerstellung nach Beendigung des Assistenten starten) auf Seite 3 des Leistungs-Assistenten deaktivieren. (Da TIP noch nicht aktiviert ist, können Sie die Anwendung weder starten noch mit der Profilerstellung beginnen.) Um die besten Ergebnisse zu erzielen, sollten Sie zuerst Sampling als Profilerstellungsmethode auswählen, insbesondere, wenn es Ihnen vor allem um die Interaktionsdaten der Datenebene geht. Der Hauptgrund: Das Sampling hat die geringste Auswirkung auf die Leistung Ihrer Anwendung.

Um die TIP-Datensammlung zu aktivieren, greifen Sie auf die Leistungssitzung im Leistungs-Assistenten zu, die soeben im Leistungs-Explorer erstellt wurde, und klicken Sie mit der rechten Maustaste, um ihre Eigenschaften anzuzeigen. Wählen Sie im Eigenschaftendialogfeld die Registerkarte Tier Interactions (Ebeneninteraktionen) aus, und aktivieren Sie das Kontrollkästchen Enable tier interaction profiling (Profilerstellung für die Ebeneninteraktion aktivieren). Klicken Sie zum Schließen des Dialogfelds auf OK. Da TIP jetzt aktiviert ist, können Sie die Profilerstellung für Ihre Anwendung starten.

Um die eigentliche Profilerstellung zu starten, klicken Sie auf die Symbolleisten-Schaltfläche Launch with Profiling (Mit Profilerstellung starten) im Leistungs-Explorer.

Ausführliche Anweisungen zum Aktivieren von TIP in Visual Studio finden Sie in dem Blogbeitrag „Exemplarische Vorgehensweise: Verwendung des Ebeneninteraktionsprofilers in Visual Studio Team System 2010“ von Habib Heydarian unter https://blogs.msdn.com/b/habibh/archive/2009/06/30/walkthrough-using-the-tier-interaction-profiler-in-visual-studio-team-system-2010.aspx.

So misst TIP die Leistung

TIP fügt während einer Profilerstellung in Ihrer Anwendung Code hinzu, der die Aufrufe in die ADO.NET-Datenschicht misst, die die Anwendung verwendet. Wenn die Profilerstellung für die Ebeneninteraktion aktiviert ist, prüft Visual Studio Profiler die MSIL (Microsoft Intermediate Language) in Ihrer Lösung und sucht nach Verweisen auf ADO.NET-Funktionen. Bevor der JIT-Compiler (Just In Time-Compiler) zum Generieren des von der Anwendung ausgeführten, nativen Codes aufgerufen wird, fügt der Profiler Anweisungen ein, die den ADO.NET-Hauptmethoden Instrumentationscode hinzufügen. Dieser Instrumentationscode verfolgt die Zeiträume für die einzelnen ADO.NET-Aufrufe.

Während die Anwendung ausgeführt wird, zeichnet TIP die Zeiterfassungsdaten auf. Wird ein Profil für Ihre Anwendung erstellt, erfasst dieser Instrumentationscode die Dauer jedes durchgeführten ADO.NET-Aufrufs und zeichnet auch den im Datenbankaufruf verwendeten Befehlszeilentext auf. Bei Ausführung der Anwendung werden Zeiterfassungsdaten für alle Datenbankzugriffe gesammelt, die unter Verwendung synchroner Methoden der ADO.NET-Klassen durchgeführt werden, darunter SQL, OLE DB, ODBC (Open Database Connectivity) und SQL CE-APIs (SQL Server Compact). TIP zeichnet ebenfalls Zeiterfassungsdaten auf, wenn Ihre Anwendung LINQ to SQL oder die Entity Framework-APIs für den Zugriff auf SQL-Datenbanken verwendet.

Die Zeitdaten werden zusammen mit anderen Daten, die während der Profilerstellungssitzung gesammelt werden, in einer Visual Studio Profiler-Datei (VSP) gespeichert. Da es sich bei dem Aufruf, den eine Anwendung an eine externe Datenbank durchführt, um einen prozessexternen Aufruf handelt, haben die Anweisungen, die TIP für die Instrumentation der Anwendung hinzufügt, sehr geringe Auswirkungen auf die Gesamtleistung der Anwendung.

Optimieren der Leistung

Ein gängiges Entwurfsmuster sieht vor, dass eine Webanwendung in eine Darstellungsschicht, eine Geschäftslogikschicht und eine Datenschicht aufgeteilt wird. Dieses Entwurfsparadigma führt dazu, dass Ihre Anwendung zur Förderung der Zuverlässigkeit, der Erweiterbarkeit und der Skalierbarkeit in Komponenten aufgegliedert wird. Die Anwendung mit mehreren Ebenen greift unter Verwendung von Geschäftslogikkomponenten auf ihre Datenschicht zu, die auf Datenentitäten logisch als Zeilen und Spalten in einem zugehörigen Tabellensatz verweisen. Die Art und Weise, auf die eine Datenbank wie SQL Server die physischen Daten verwaltet, die mit der Datenbanktabelle verbunden sind, wird durch den Entwurf für Ihre Anwendung transparent gemacht.

Aus Gründen der Zuverlässigkeit und Skalierbarkeit konfigurieren umfangreiche Webanwendungen mehrere Computer häufig in Pools, die für die Verarbeitungslogik zuständig sind, die mit den einzelnen Schichten der Anwendung verbunden ist. Wenn mehrere Computer mehrere Ebenen unterstützen, stellt dies eine besondere Herausforderung für die Leistungsanalyse dar, weil die Überwachung eines einzelnen Computers ein unvollständiges Bild der Anwendung ergibt.

Wird beispielsweise ein Datenbanksystem wie SQL Server für die Verwaltung und Vermittlung des Zugriffs auf den Datenspeicher der Anwendung verwendet, wird eine von der Anwendungslogik isolierte Datenschicht erstellt. Die Anwendungsdaten, die in einer Datenbank wie SQL Server enthalten sind, werden in einem gesonderten Prozessadressraum mit eigenem Datenspeicher verwaltet. Die Datenbank, die die Anwendungsdaten enthält, kann sich auf demselben physischen Computer befinden wie die Anwendung; der Zugriff auf die Datenbank erfolgt jedoch wahrscheinlich unter Verwendung von Netzwerkprotokollen über einen anderen Computer.

SQL-Befehle, die die Anwendung an eine externe Datenbank weitergibt und die dort ausgeführt werden, werden als prozessexterne Aufrufe bezeichnet. Samplingprofile erkennen, dass die Anwendung inaktiv ist, während sie auf die Ausführung dieser prozessexternen Aufrufe wartet; sie enthalten jedoch keine Informationen über den Grund, aus dem die Anwendung wartet, oder über die Dauer dieser Verzögerungen. Instrumentations- und Parallelitätsprofile messen zwar die Dauer dieser Verzögerungen, enthalten aber keine Informationen darüber, welche SQL-Befehle ausgegeben werden oder warum deren Ausführung so viel Zeit in Anspruch nimmt.

In Anwendungen mit mehreren Ebenen, die mit einer externen Datenbank kommunizieren, trägt die Datenbankkomponente oft maßgeblich zur allgemeinen Reaktionszeit der Anwendung bei. Die Visual Studio-Profilerstellungstools beinhalten verschiedene Profilerstellungsmethoden, einschließlich Sampling, Instrumentation, Speicherbelegung und Parallelität, aber keine dieser Methoden ist hilfreich, um Leistungsprobleme in Verbindung mit dem Zugriff auf eine externe Datenbank ohne TIP-Daten zu identifizieren.

Mit TIP-Daten können Sie Detailinformationen zu datenbankbezogenen Verzögerungen und ihren Ursachen anzeigen. In Verbindung mit anderen Leistungstools von Ihrem Datenbankanbieter können Sie auch erkennen, welche Maßnahmen zur Verbesserung der Leistung einer Anwendung ergriffen werden sollten, die stark von der Datenbankleistung abhängt.

Da TIP dem Anwendungscode Instrumentationscode hinzufügt, können mit Datenbankbefehlen verbundene Zeiterfassungsdaten unabhängig davon gesammelt werden, wo sich die Datenbankinstanz, auf die zugegriffen wird, physisch befindet. Zeiterfassungsdaten können beispielsweise für prozessexterne Aufrufe an eine SQL Server-Instanz gesammelt werden, die sich physisch auf demselben Computer befindet wie die Anwendung – ein typisches Szenario bei Komponententest. Wenn dieselbe Anwendung für die Integration oder Auslastungstests mit einer SQL Server-Instanz auf einem anderen physischen Computer bereit ist, kann TIP mit dem Sammeln von Messdaten für diese Konfiguration fortfahren. Anhand der TIP-Messungen können Sie sogar die Leistung dieser beiden verschiedenen Konfigurationen vergleichen.

TIP ermöglicht den Vergleich und die Gegenüberstellung der Auswirkung zahlreicher verfügbarer Leistungs- und Optimierungsoptionen für externe Datenbanken, darunter Konfiguration des Cachespeichers, physische Datenspeichergeräte, Datenbankpartitionierung, Datenbankindizierung und Datenbanktabellenentwurf. Darüber hinaus können Sie direkt die Leistungsauswirkung der Ausführung von SQL Server auf einem virtuellen Computer messen.

Grundlagen zu TIP-Berichten

Wird eine Profilerstellungssitzung mit aktivierter TIP-Funktion abgeschlossen, werden die Zeiterfassungsdaten, die mit einer beliebigen Interaktion der Anwendung mit der zugehörigen ADO.NET-Datenschicht verbunden sind, in der Ansicht Tier Interactions (Ebeneninteraktionen) zusammengefasst. Abbildung 1 zeigt ein Beispiel des Profilers bei aktivierter TIP-Datensammlung, wenn während der Profilerstellung ADO.NET-Aktivität erfolgt.

image: A Visual Studio Profiler Tier Interaction Report

Abbildung 1 Ein Bericht zur Visual Studio Profiler-Ebeneninteraktion

Die obere Hälfte des Berichts ist eine Zusammenfassung der gesammelten Profilerstellungsdaten. Für ASP.NET-Anwendungen wird die Ansicht nach URL geordnet. Der Bericht gruppiert serverseitige Antwortzeiten der GET-Anforderungen von Webanwendungen nach URL.

Unter der Anwendungsschicht zeigt der Bericht die einzelnen Verbindungen zu einer Datenbankebene (in diesem Beispiel die Beispieldatenbank AdventureWorks). Er misst und meldet den Anteil der serverseitigen Verarbeitungszeit für ASP.NET-Anforderungen, der mit synchronen Datenbankaufrufen unter Verwendung von ADO.NET verbunden ist. In diesem Beispiel werden drei Zusammenfassungszeilen angezeigt, in denen jeweils die Datenbankaktivität aufgeführt wird, die mit den drei verschiedenen ASP.NET-Seiten in der Website verbunden sind, für die das Profil erstellt wird. Für jede während der Profilerstellung identifizierte ASP.NET-Seite werden die Anzahl der während der Profilerstellung verarbeiteten ASP.NET-Anforderungen sowie die serverseitigen Reaktionszeiten für die einzelnen generierten Antwortnachrichten gemeldet.

Weitere Zusammenfassungszeilen zeigen die Reaktionszeitdaten für andere GET-Anforderungen, einschließlich der Anforderungen für Stylesheets, des Javascript-Codes und der in den Seiten verlinkten Bilder. Alle Datenbankaufrufe, die der Profiler nicht mit einer bestimmten ASP.NET-Anforderung verknüpfen kann, werden in der Kategorie für andere Anforderungen gruppiert.

Wenn Sie ein Profil für eine Windows-Desktop- oder Konsolenanwendung erstellen, die eine Datenschicht nutzt, ordnet der Bericht die ADO.NET-Aktivität unter dem Prozessnamen ein.

Unter den einzelnen Webseiten-Zusammenfassungszeilen befindet sich jeweils eine einzelne Zusammenfassungszeile, in der die Anzahl der synchronen Datenbankabfragen während der ASP.NET-Verarbeitung nach Datenbankverbindung angeordnet angezeigt wird. In diesem Beispiel können Sie sehen, dass sechs ASP.NET-Anforderungen in CustomerQuery.aspx über eine einzelne Datenbankverbindung verarbeitet wurden. Für die Verarbeitung dieser sechs Anforderungen auf dem Server wurden bei einer durchschnittlichen Antwortzeit von 160 Millisekunden insgesamt 0,959 Sekunden benötigt. Diese Anforderungen haben zwölf SQL-Abfragen ausgegeben, für deren Durchführung circa 45 Millisekunden benötigt wurden. Die Wartezeit für die Datenbankanfragen machte nur fünf Prozent der verstrichenen Zeit aus, die mit dem Generieren der Antwortnachrichten für diese Website verbunden ist.

Wenn Sie eine der Datenbankverbindungs-Zusammenfassungszeilen markieren, werden in der unteren Hälfte der Ebeneninteraktionsansicht die spezifischen, von der Anwendung ausgegebenen SQL-Befehle angezeigt. Die SQL-Befehle werden nach ausgegebenem Befehlszeilentext gruppiert und nach verstrichener Zeit innerhalb der Seitengruppe sortiert.

In dem Beispiel wurde ein SQL-Befehl drei Mal ausgegeben, ein anderen Befehl sechs Mal, und dritte Abfrage wurde drei Mal ausgegeben. In der Detailansicht werden die verstrichenen Zeiten der einzelnen spezifischen Abfragen, die im Zusammenfassungsbericht in einer einzelnen Zeile zusammengefasst werden, gesondert aufgeführt. Sie können die gesamte verstrichene Zeit anzeigen, den Durchschnitt für alle Instanzen des Befehls und die minimalen und maximalen Verzögerungen, die für jede Abfrage beobachtet wurden.

Wenn Sie auf die Detailzeile eines SQL-Befehls doppelklicken, wird der gesamte Text des ausgegebenen SQL-Befehls in einem Fenster für Datenbankbefehlstext angezeigt. Dies ist der eigentliche Befehl, den die Anwendung während der Ausführung über die ADO.NET-Schnittstelle an die Datenbank weitergegeben hat. Wenn die Anforderung für die Ausführung einer gespeicherten Prozedur gilt, wird der spezifische Aufruf an die gespeicherte Prozedur angezeigt.

Ein Beispiel für LINQ to SQL

Im Folgenden wird ein einfaches Verwendungsbeispiel für TIP mit einer ASP.NET-Anwendung aufgeführt, die stark von Zugriffsinformationen aus einer Datenbank abhängt.

TIP kann besonders hilfreich bei Anwendungen sein, die LINQ to SQL für den Zugriff Daten verwenden, die in einer externen SQL Server-Datenbank gespeichert sind, da LINQ dazu neigt, den Entwickler einen Schritt weiter von der physischen Datenbank und ihren Leistungseigenschaften zu entfernen. Mit LINQ to SQL generieren die E:R-Diagramme (Entity:Relationship), die Sie in Object Relational Designer erstellen, Klassen, die dann von Visual Studio automatisch als Vorlagen für die Erstellung syntaktisch korrekter SQL-Befehle verwendet werden.

Da LINQ to SQL einen Großteil der Bedenken hinsichtlich der SQL-Sprachcodierung beseitigt und benutzerfreundlich ist, wird ebenfalls darauf abgezielt, wichtige Leistungsaspekte zu kaschieren, die mit dem Entwurf, der Konfiguration und Optimierung der Datenbank verbunden sind. Wie dieses Beispiel zeigt, können Sie unter Verwendung von LINQ problemlos eine komplexe Abfrage erstellen, in der mehrere Tabellen verknüpft werden, ohne dass Sie die Auswirkungen Ihrer Vorgehensweise auf die Leistung berücksichtigen müssen.

Mit TIP können Sie den eigentlichen SQL-Befehlstext anzeigen, den LINQ to SQL generiert, und Messwerte aus der Laufzeitausführung dieser SQL-Abfragen sammeln. Mit dem vorliegenden SQL-Befehlstext können Sie dann auf andere Datenbankoptimierungstools zugreifen, mit denen Sie einen besseren Überblick über die Auswirkungen eines bestimmten LINQ to SQL-Vorgangs auf die Leistung erhalten.

Im vorliegenden Beispiel führt eine Web Form-Anwendung eine Abfrage der AdventureWorks Sales.Customer-Tabellen durch, die eine bestimmte Kunden-ID verwenden, um den Bestellverlauf des entsprechenden Kunden abzurufen. Zu den an dieser Abfrage beteiligten AdventureWorks-Tabellen zählen die Tabellen Customer, SalesOrderHeader, SalesOrderDetail und StateProvince, wie in der Object Relational Designer-Ansicht in Abbildung 2 dargestellt.

image: The AdventureWorks Tables Used to Query Sales.Customer Information

Abbildung 2 Die zur Abfrage von Sales.Customer-Informationen verwendeten AdventureWorks-Tabellen

Sollen zusätzlich zum Bestellverlauf auch die Post- und die E-Mail-Adresse eines Kunden angezeigt werden, muss auf die Tabellen CustomerAddress, Address und Contact zugegriffen werden. Wie in Object Relational Designer dargestellt, enthalten die AdventureWorks-Tabellen Primär- und Fremdschlüssel wie CustomerID, SalesOrder und ContactID, die eine logische Verknüpfung dieser Tabellen ermöglichen.

Der C#-Code für die Erstellung einer AdventureWorks-Kundenabfrage unter Verwendung von LINQ to SQL wird in Abbildung 3 dargestellt. In diesem Fall ist custid der spezifische angeforderte CustomerID-Wert. Diese Abfrage gibt eine customeryquery-Sammlung zurück, die eine einzelne Datenzeile mit den Datenfeldern enthält, die in der select new-Klausel aufgeführt werden.

Abbildung 3 LINQ to SQL-Kundenabfrage

var customerquery = 
  from customers in db.Customers
  from custaddrs in db.CustomerAddresses
  from addrs in db.Addresses
  where (customers.CustomerID == custid &&
         customers.CustomerID == custaddrs.CustomerID &&
         custaddrs.AddressID == addrs.AddressID)

  select new {
    customers.CustomerID,
    customers.CustomerType,
    addrs.AddressLine1,
    addrs.AddressLine2,
    addrs.City,
    addrs.StateProvince,
    addrs.PostalCode,
    customers.TerritoryID
  };

customeryquery kann dann an ein Steuerelement auf einer ASP.NET-Webseite gebunden werden:

DetailsView1.DataSource = customerquery;
DetailsView1.DataBind();

Jetzt kann eine Abfrage zum Abrufen des Bestellverlaufs dieses Kunden erstellt werden:

var orderquery = 
  from orderhdr in db.SalesOrderHeaders
  where (orderhdr.CustomerID == custid)
  orderby orderhdr.OrderDate
  select new {
    Date = orderhdr.OrderDate.ToShortDateString(),
    orderhdr.AccountNumber,
    InvoiceNo = orderhdr.SalesOrderID,
    orderhdr.TotalDue
  };

Wenn dieser LINQ to SQL-Vorgang ausgeführt wird, enthält orderquery eine Zeile, die den einzelnen Zeilen in der Tabelle OrderHdr entspricht, die mit einer bestimmten Kunden-ID verbunden ist. Die orderquery-Sammlung enthält mehrere Zeilen, wenn im Kundenverlauf angegeben ist, dass mehrere Verkaufstransaktionen stattgefunden haben.

Diese Abfragen sehen oberflächlich einfach aus. Aber mit TIP können Sie die Auswirkungen dieser anscheinend einfachen LINQ to SQL-Vorgänge auf die Leistung erkennen.

Verwenden von TIP-Daten für die Optimieren

Im Folgenden wird customerquery näher betrachtet. Zur Laufzeit verwendet LINQ to SQL die in der LINQ-Anweisung implizierten SELECT-Vorgänge der logischen Datenbank, um einen gültigen SQL-Befehl zu erstellen, der Daten aus den folgenden vier AdventureWorks-Tabellen verknüpft: Customers, CustomerAddresses, Addresses und aus der statischen Tabelle StateProvince. Der hier dargestellte LINQ to SQL-Code gibt dies nicht wieder.

Wenn Sie diesen Code unter Visual Studio Profiler ausführen, meldet die TIP-Instrumentation die Anzahl der Abfrageausführungen und misst den Verzögerungszeitraum der Website, während diese auf ihre Ausführung wartet. Hierbei handelt es sich tatsächlich um den Vorgang, der während der in Abbildung 1 dargestellten Profilerstellung sechs Mal ausgeführt wurde.

Wie Sie gesehen haben, steht der vom LINQ to SQL-Code generierte SQL-Befehl darüber hinaus auch zur Verfügung, wenn ein Profil für die Anwendung erstellt wird. In Abbildung 4 wird der eigentliche SQL-Befehl für diesen Vorgang gezeigt.

Abbildung 4 SQL-Befehl für „customerquery“

SELECT [t0].[CustomerID], [t0].[CustomerType], [t2].[AddressLine1], [t2].[AddressLine2], [t2].[City], [t3].[StateProvinceID], [t3].[StateProvinceCode], [t3].[CountryRegionCode], [t3].[IsOnlyStateProvinceFlag], [t3].[Name], [t3].[TerritoryID], [t3].[rowguid], [t3].[ModifiedDate], [t2].[PostalCode], [t0].[TerritoryID] AS [TerritoryID2]
FROM [Sales].[Customer] AS [t0]
CROSS JOIN [Sales].[CustomerAddress] AS [t1]
CROSS JOIN [Person].[Address] AS [t2]
INNER JOIN [Person].[StateProvince] AS [t3] ON [t3].[StateProvinceID] = [t2].[StateProvinceID]
WHERE ([t0].[CustomerID] = @p0) AND ([t0].[CustomerID] = [t1].[CustomerID]) AND ([t1].[AddressID] = [t2].[AddressID])

Der SQL-Befehlstext beinhaltet ein Token (wird hier als @p0 bezeichnet) zur Darstellung des Kunden-ID-Parameters, den LINQ in die Abfrage eingibt.

Da jetzt der eigentliche von LINQ generierte SQL-Befehlstext verfügbar ist, kann man die Auswirkung des Datenbankentwurfs auf die Leistung der Abfrage erkennen.

Sie können diesen SQL-Befehl in SQL Server Management Studio ausführen und seinen Ausführungsplan prüfen (siehe Abbildung 5). Für den Zugriff auf den Ausführungsplan für diese Abfrage müssen Sie einen Befehl hinzufügen, um auf die entsprechende Datenbank zu verweisen:

USE AdventureWorks ;
GO

Kopieren Sie anschließend den SQL-Befehlstext aus dem TIP-Bericht. Denken Sie daran, das Token @p0 durch einen gültigen CustomerID-Wert aus der Datenbank zu ersetzen. Führen Sie dann diese Beispielabfrage in SQL Server Management Studio aus. Greifen Sie auf den Ausführungsplan zu, der zeigt, wie der Abfrageoptimierer die logische Anfrage in einen physischen Ausführungsplan übersetzt hat.

image: The Execution Plan for the Example LINQ to SQL Operation

Abbildung 5 Der Ausführungsplan für den LINQ to SQL-Beispielvorgang

Im vorliegenden Beispiel zeigt der Ausführungsplan für die Abfrage, dass die SELECT-Anweisung unter Verwendung eines gruppierten Index im Feld CustomerID auf die Tabelle Customer zugegriffen hat und genau eine Zeile in der Tabelle zurückgegeben wurde. In SQL Server Management Studio können Sie den Mauszeiger über einen Vorgang bewegen, um seine Eigenschaften anzuzeigen, oder den Vorgang markieren und mit der rechten Maustaste klicken, um das Eigenschaftenfenster anzuzeigen. Auf diese Weise können Sie durch alle verbleibenden Vorgänge blättern, die der Befehl anfordert. Jeder der drei nachfolgenden JOIN-Vorgänge, die die erste SELECT-Anweisung für die Tabelle Customer erweitern, greift ebenfalls unter Verwendung des zugehörigen gruppierten Index auf eine Tabelle zu und gibt ein einzelne eindeutige Zeile zurück.

Sie können also sehen, dass zur Verarbeitung dieser Abfrage auf insgesamt vier Zeilen zugegriffen werden muss, von denen sich jede in einer anderen Tabelle der AdventureWorks-Datenbank befindet. Jeder Zugriff wird effizient unter Verwendung des eindeutigen Primärschlüssels einer Tabelle durchgeführt.

Auf ähnliche Weise können Sie TIP einsetzen, um den SQL-Befehl für den orderquery-Code anzuzeigen, und ihn zur Anzeige seines Ausführungsplans in SQL Server Management Studio einzugeben (siehe Abbildung 6). Diese Abfrage greift auf eine einzelne Tabelle namens OrderHdr zu und verwendet dabei die CustomerID als Primärschlüssel. Sie muss also auf einen herkömmlichen, nicht gruppierten Index sowie den gruppierten Index in SalesOrderHeaderID zugreifen.

image: The Execution Plan for orderquery

Abbildung 6 Der Ausführungsplan für „orderquery“

Diese spezielle Instanz der Abfrage gibt neun Zeilen zurück. Die orderby-Klausel im LINQ to SQL-Code wird in eine SQL ORDER BY-Klausel übersetzt, die einen zusätzlichen Sort-Vorgang für den Resultset von SELECT zur Folge hat. Dieser Vorgang stellt gemäß Schätzung durch SQL Server Plan Optimizer 40 % des Gesamtaufwands der Abfrageausführung dar.

Auswählen eines Profilerstellungskontexts

TIP wurde zur Ergänzung vorhandener Visual Studio-Profilerstellungsmethoden entwickelt, um spezifische Messwerte zu Datenebeneninteraktionen zu sammeln. TIP stellt eine sekundäre Funktion zur Datensammlung dar. Diese kann nur durchgeführt werden, wenn eine primäre Profilerstellungsmethode festgelegt wird. TIP-Daten können während der Sampling-, Instrumentations- und Parallelitäts-Profilerstellungen für jede Anwendung gesammelt werden, die unter Verwendung von ADO.NET mit einer Datenebene kommuniziert.

Vorausgesetzt, Sie müssen eine primäre Profilerstellungsmethode für eine Anwendung auswählen, für die Sie TIP-Daten sammeln möchten – welche Methode sollten Sie verwenden? Werfen Sie einen Blick auf die Aspekte für die Auswahl einer primären Profilerstellungsmethode.

Sind die mit den Datenebeneninteraktionen verbundenen Verzögerungen der Hauptschwerpunkt der Leistungsuntersuchung? Ist dies der Fall, ist das Sampling als primäre Profilerstellungsmethode zu empfehlen, da es in der Regel die am wenigsten intrusive Form der Profilerstellung ist.

Wenn die Datenebenenverzögerung nicht den Hauptschwerpunkt der Leistungsuntersuchung darstellen, wählen Sie eine Profilerstellungsmethode danach aus, welche Methode voraussichtlich die Messdaten liefert, die dem aktuellen Kontext am ehesten entsprechen. Wenn Sie beispielsweise ein Problem im Zusammenhang mit der gleichzeitigen Ausführung mehrerer Threads untersuchen, sammeln Sie Parallelitätsdaten. Untersuchen Sie ein Problem im Zusammenhang mit einer CPU-gebundenen Anwendung, sammeln Sie Samplingdaten. Zusätzliche Anleitungen zur Auswahl einer primären Sammlungsmethode finden Sie in dem Artikel „Gewusst wie: Auswahl von Sammlungsmethoden“.

Wenn Sie noch nicht mit dem Datenschichtcode vertraut sind, können die primären Profilerstellungsdaten bei der Suche nach dem genauen Code hilfreich sein, aus dem diese ADO.NET-Aufrufe kommen. TIP zeichnet beim Sammeln von Zeiterfassungsdaten für synchrone prozessexterne ADO.NET-Aufrufe keine Aufrufliste auf. Wenn Sie wissen müssen, an welcher Stelle in den Anwendungsaufrufen die ADO.NET-Methoden erstellt werden, sind instrumentierte Profile am hilfreichsten. Samplingdaten können ebenfalls hilfreich sein, sind jedoch in diesem Hinblick nicht so präzise wie ein instrumentiertes Profil.

Zusätzlich zu den Messwerten der Ebeneninteraktion können Sie Daten zu Ressourcenkonflikten sammeln. Das Sammeln von Ressourcenkonfliktdaten verursacht jedoch meist einen höheren Aufwand als das Sampling, und die Konfliktdaten bieten wahrscheinlich keine Hilfe bei der Ermittlung des Ursprungs bestimmter ADO.NET-Aufrufe. Untersuchungen, die die Profilerstellung für .NET-Speicherzuordnungen erfordern, haben normalerweise große Auswirkungen und profitieren eher nicht von gesammelten Messwerten der Ebeneninteraktion.

Samplingprofile

Häufig sind die Datenebeneninteraktionen selbst der Hauptschwerpunkt einer Leistungsuntersuchung. In diesem Fall werden in der Regel die besten Ergebnisse erzielt, wenn das Sampling als primäre Profilerstellungsmethode ausgewählt wird. Das Sampling wird in diesem Fall bevorzugt, da diese Profilerstellungsmethode normalerweise die geringsten Auswirkungen auf die Leistung einer Anwendung hat. Samplingprofile können sich auch bei der Navigation zu dem Quellcode als hilfreich erweisen, aus dem die ADO.NET-Aufrufe mit der höchsten Leistungsbeeinträchtigung kommen.

Mit den prozessextern ausgeführten Datenebenenfunktionen geben die Anweisungsausführungsbeispiele, die in einem Samplingprofil gesammelt werden, normalerweise nicht den Zeitraum wieder, während dessen eine Anwendung darauf wartet, dass synchrone Aufrufe über eine ADO.NET-Schnittstelle abgeschlossen werden. In den Zeiträumen, in denen der Ausführungsthread einer Anwendung auf den Abschluss dieser prozessexternen Aufrufe wartet, wird das Anwendungsthread gesperrt. Es werden keine entsprechenden Ausführungsbeispiele gesammelt. Bei Verwendung des Samplings stellt das Sammeln von TIP-Daten die beste Möglichkeit dar, um die Ursache für Anwendungsverzögerungen zu erkennen, die auf synchrone Aufrufe an eine Datenebene zurückzuführen sind.

Die von TIP verwendete Instrumentation zeichnet beim Sammeln von Zeiterfassungsdaten keine Anruflisten auf. Wenn Sie ein Profil für eine Anwendung mit mehreren erstellen und den Code nicht vollständig kennen, kann es schwierig sein, den genauen Ursprung der Aufrufe in der Datenschicht zu ermitteln. Anhand von Samplingprofilen können Sie auch erkennen, wo im Anwendungscode Aufrufe zu diesen ADO.NET-Schnittstellen durchgeführt werden. Wenn die Anwendung häufig Aufrufe an ADO.NET-Schnittstellen durchführt, werden wahrscheinlich einige Beispiel gesammelt, die den für ADO.NET-Module, einschließlich System.Data.dll und System.Data.Linq.dll, aufgewendeten Zeitraum wiedergeben.

Bei der Überprüfung der Samplingdaten und dem Vergleich der Daten mit den Messwerten der Ebeneninteraktion sollten Sie berücksichtigen, dass keine Samplingdaten für einen Anwendungsthread gesammelt werden, während er gesperrt ist und auf den Abschluss eines synchronen Datenbankaufrufs wartet. Von den während der Ausführung gesammelten Beispielen sind die prozessexternen Verzögerungen ausgenommen, die explizit von TIP gemessen werden. Zu erwarten ist jedoch eine grobe Korrelation zwischen den in ADO.NET-Methoden gesammelten Beispielen und der Anzahl der ADO.NET-Befehle, die TIP verfolgt und misst. In solchen Fällen können Samplingprofile bei der Navigation zu dem Quellcode hilfreich sein, aus dem von TIP gemessene ADO.NET-Aufrufe kommen.

Enthält eine Anwendung SQL-Abfragen, die umfangreiche Resultsets zurückgeben, die an ein datengebundenes Steuerelement in einem Formular gebunden sind, werden Sie wahrscheinlich eine erhebliche Anzahl an Ausführungsbeispielen in der DataBind-Methode des Steuerelements finden. Zu wissen, welche DataBind-Methoden im Samplingprofil angezeigt werden, kann ebenfalls bei der Navigation zu dem Quellcode hilfreich sein, aus dem die ADO.NET-Aufrufe kommen.

Instrumentierte Profile

Wenn instrumentierte Profile gesammelt werden, enthalten die Zeiterfassungsdaten für die von der Instrumentation erfassten Methoden bereits die Wartezeit für den Abschluss der prozessexternen Aufrufe innerhalb einer Methode. Die in einem instrumentierten Profil erfassten Zeiterfassungsdaten werden gesammelt, indem der Aktivierungs- und der Endzeitpunkt der einzelnen Anwendungsmethoden gemessen wird, die für die Instrumentation ausgewählt werden. Zeiterfassungsdaten für Methoden in einer Anwendung, die unter Verwendung von ADO.NET-Aufrufen eine Schnittstelle zu einer Datenebenenschicht aufweisen, berücksichtigen implizit die Verzögerung bei der Ausführung prozessexterner Aufrufe.

Die von TIP gesammelten Zeiterfassungsdaten identifizieren prozessexterne Verzögerungen explizit und führen entsprechende gesonderte Messungen durch. Die von der Profilerstellung für die Ebeneninteraktion gemessene Verzögerung sollte eine Teilmenge des gesamten für die Methode aufgewendeten Zeitraums sein, wie er während einer instrumentierten Profilerstellung gemessen wurde. Basierend auf diesem Wissen sollten Sie in der Lage sein, die Zeiterfassungsdaten aus der Profilerstellung für die Ebeneninteraktion den Zeiterfassungsdaten auf Methodenebene in einem instrumentierten Profil zuzuordnen, um die Methode zu ermitteln, aus der der Aufruf an die Datenschicht kommt.

Wenn die Instrumentation auf Methodenebene ausreicht, um den Ursprung der ADO.NET-Aufrufe zu ermitteln, kann die instrumentierte Profilerstellung bedenkenlos verwendet werden. Instrumentierte Profile sind jedoch in der Regel sehr viel intrusiver als Samplingprofile, verursachen einen größeren Aufwand und generieren meist erheblich größere VSP-Sammlungsdateien. Wenn die Anwendung Methoden verwendet, die mehrere Aufrufe an ADO.NET-Funktionen durchführen, können Sie darüber hinaus anhand der durch Instrumentation gesammelten Daten nur zur Methodenebene navigieren und nicht zwischen den verschiedenen ADO.NET-Aufrufen unterscheiden, die in einer einzelnen Methode eingebettet sind.

Mehr Daten, bitte

Die Erstellung von Anwendungen mit mehreren Ebenen ist ein Entwurfsmuster, das die Zuverlässigkeit und Skalierbarkeit fördert, führt jedoch zu Herausforderungen bei der Leistungsüberwachung, wenn Anwendungskomponenten auf verschiedenen Computern ausgeführt werden.

Eine einfache Ansicht einer Anwendung mit mehreren Ebenen, die nicht alle ihre vernetzten Schichten umfasst, ergibt kein vollständiges Bild der Anwendungsleistung. Wie Sie sehen konnten, kann TIP wichtige Zeiterfassungsdaten bereitstellen, die andernfalls fehlen. Das Beispiel in diesem Artikel zeigt, dass die Zeiterfassungsdaten mit anderen Leistungsdaten aus den standardmäßigen Tools des Datenbankadministrators erweitert werden können.

Mark Friedman ist Architekt im Visual Studio Ultimate-Team bei Microsoft. Er ist der Autor zweier Bücher zur Leistung von Windows und schreibt unter blogs.msdn.com/ddperf/ regelmäßig Blogbeiträge zu Leistungsproblemen.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Daryush Laqab und Chris Schmich