Leistungsfähigkeit der CLR-Integration
In diesem Thema werden einige der Entwurfsoptionen beschrieben, die die Leistungsfähigkeit der MicrosoftSQL Server-Integration in Microsoft .NET Framework Common Language Runtime (CLR) verbessern.
Der Kompilierungsprozess
Wenn während der Kompilierung von SQL-Ausdrücken ein Verweis auf eine verwaltete Routine gefunden wird, wird ein Microsoft Intermediate Language (MSIL)-Stub generiert. Dieser Stub enthält Code zum Marshallen der Routineparameter von SQL Server zu CLR, zum Aufrufen der Funktion und zur Rückgabe des Ergebnisses. Dieser "Verbindungscode" basiert auf dem Parametertyp und der Parameterrichtung (IN, OUT oder Verweis).
Der Verbindungscode ermöglicht typspezifische Optimierungen und stellt die wirksame Durchsetzung der SQL Server-Semantik sicher, wie beispielsweise NULL-Zulässigkeit, Einschränkungsfacets, Nach-Wert-Verarbeitung und standardmäßige Ausnahmebehandlung. Durch die Codegenerierung für genaue Argumenttypen vermeiden Sie Kosten für Typenumwandlung und die Erstellung von Wrapperobjekten ("Boxing" genannt) über die Aufrufgrenze hinweg.
Der generierte Stub wird dann mithilfe des Just-in-Time(JIT)-Kompilierungsdiensts von CLR in systemeigenen Code kompiliert und für die spezifische Hardwarearchitektur, in der SQL Server ausgeführt wird, optimiert. Die JIT-Dienste werden auf Methodenebene aufgerufen und ermöglichen es der SQL Server-Hostumgebung, eine einheitliche Kompilierungseinheit zu erstellen, die sowohl SQL Server als auch die CLR-Ausführung beinhaltet. Sobald der Stub kompiliert ist, wird der resultierende Funktionszeiger zur Laufzeitimplementierung der Funktion. Dieses Codegenerierungsverfahren stellt sicher, dass keine zusätzlichen Aufrufkosten durch Reflexion oder Metadatenzugriff zur Laufzeit entstehen.
Schnelle Übergänge zwischen SQL Server und CLR
Der Kompilierungsprozess erzeugt einen Funktionszeiger, der zur Laufzeit über systemeigenen Code aufgerufen werden kann. Bei benutzerdefinierten Skalarwertfunktionen erfolgt dieser Funktionsaufruf auf Zeilenbasis. Um die Kosten für den Übergang zwischen SQL Server und CLR zu minimieren, verfügen Anweisungen, die verwaltete Aufrufe beinhalten, über einen Startschritt zur Identifizierung der Zielanwendungsdomäne. Dieser Identifizierungsschritt reduziert die Kosten für den Übergang der einzelnen Zeilen.
Leistungsaspekte
Im Folgenden werden Informationen über Leistungsaspekte in Bezug auf die CLR-Integration in SQL Server gegeben. Detailliertere Informationen finden Sie unter "Using CLR Integration in SQL Server 2005" auf der MSDN-Website. Allgemeine Informationen über die Leistungsfähigkeit von verwaltetem Code finden Sie unter "Improving .NET Application Performance and Scalability" auf der MSDN-Website.
Benutzerdefinierte Funktionen
CLR-Funktionen profitieren im Vergleich zu benutzerdefinierten Transact-SQL-Funktionen von einem schnelleren Aufrufpfad. Zudem bietet verwalteter Code im Vergleich zu Transact-SQL deutliche Leistungsvorteile in Bezug auf den prozeduralen Code, die Berechnung und die Zeichenfolgenbearbeitung. CLR-Funktionen, die einen hohen Berechnungsaufwand mit sich bringen und keinen Datenzugriff ausführen, sollten besser in verwaltetem Code geschrieben werden. Allerdings sind Transact-SQL-Funktionen für den Datenzugriff effizienter als die CLR-Integration.
Benutzerdefinierte Aggregate
Verwalteter Code ist deutlich leistungsfähiger als die cursorbasierte Aggregation. Verwalteter Code ist in der Regel etwas langsamer als integrierte SQL Server-Aggregatfunktionen. Wir empfehlen daher, eine systemeigene integrierte Aggregatfunktion zu verwenden, sofern sie zur Verfügung steht. In Fällen, in denen die benötigte Aggregation nicht vom System unterstützt wird, sollten Sie aus Gründen der Leistungsfähigkeit ein CLR-benutzerdefiniertes Aggregat einer cursorbasierten Implementierung den Vorzug geben.
Streaming-Tabellenwertfunktionen
Anwendungen müssen oft als Reaktion auf einen Funktionsaufruf eine Tabelle als Ergebnis zurückgeben. Beispiele dafür sind das Lesen von Tabellendaten aus einer Datei als Teil eines Importvorgangs oder die Konvertierung von durch Trennzeichen getrennten Werte in eine relationale Darstellung. In der Regel erreichen Sie dies durch Materialisieren und Auffüllen der Ergebnistabelle, bevor sie vom Aufrufer verwendet werden kann. Mit der Integration von CLR in SQL Server wird ein neuer Erweiterungsmechanismus eingeführt, der als Streaming-Tabellenwertfunktion (STVF) bezeichnet wird. Verwaltete STVF sind leistungsfähiger als vergleichbare Implementierungen mit erweiterten gespeicherten Prozeduren.
STVF sind verwaltete Funktionen, die eine IEnumerable-Schnittstelle zurückgeben. IEnumerable besitzt Methoden für das Navigieren im Resultset, der von der STVF zurückgegeben wird. Wenn die STVF aufgerufen wird, wird die zurückgegebene IEnumerable-Schnittstelle direkt mit dem Abfrageplan verbunden. Der Abfrageplan ruft IEnumerable-Methoden auf, wenn er Zeilen abrufen muss. Dieses Iterationsmodell ermöglicht es, dass Ergebnisse sofort nach Abruf der ersten Zeile verarbeitet werden. Es muss nicht gewartet werden, bis die gesamte Tabelle aufgefüllt ist. Dadurch wird zudem der durch den Funktionsaufruf benötigte Arbeitsspeicher stark reduziert.
Arrays oder Cursor
Wenn Transact-SQL-Cursor Daten traversieren müssen, die als Array einfacher auszudrücken sind, kann verwalteter Code verwendet und die Leistung dadurch gesteigert werden.
Zeichenfolgendaten
SQL Server Zeichendaten, wie z. B. varchar, können in verwalteten Funktionen vom Typ SqlString oder SqlChars sein. SqlString-Variablen erstellen im Arbeitsspeicher eine Instanz des gesamten Werts. SqlChars-Variablen stellen eine Streamingschnittstelle bereit, mit der eine höhere Leistung und bessere Skalierbarkeit erreicht wird, die jedoch nicht zum Erstellen einer Instanz des gesamten Werts im Arbeitsspeicher verwendet werden kann. Dies ist besonders für Daten großer Objekte (Large Objects, LOB) wichtig. Darüber hinaus kann über eine von SqlXml.CreateReader() zurückgegebene Streamingschnittstelle auf XML-Serverdaten zugegriffen werden.
CLR und erweiterte gespeicherte Prozeduren im Vergleich
Die Microsoft.SqlServer.Server-APIs (Application Programming Interfaces), die es verwalteten Prozeduren ermöglichen, Resultsets zurück an den Client zu senden, sind leistungsfähiger als die von erweiterten gespeicherten Prozeduren verwendeten Open Data Services(ODS)-APIs. Darüber hinaus unterstützen die System.Data.SqlServer-APIs Datentypen wie xml, varchar(max), nvarchar(max) und varbinary(max), die in SQL Server 2005 eingeführt wurden, während ODS-APIs nicht für die Unterstützung der neuen Datentypen erweitert wurden.
Mit verwaltetem Code verwaltet SQL Server die Verwendung von Ressourcen wie beispielsweise Arbeitsspeicher, Threads und Synchronisierung. Das rührt daher, dass die verwalteten APIs, die diese Ressourcen verfügbar machen, auf dem SQL Server-Ressourcen-Manager implementiert werden. Hingegen verfügt SQL Server über keine Sicht für oder Kontrolle über die Ressourcenverwendung der erweiterten gespeicherten Prozedur. Wenn eine erweiterte gespeicherte Prozedur beispielsweise zu viel CPU- oder Speicherressourcen belegt, gibt es keine Möglichkeit, dies mit SQL Server zu erkennen oder zu kontrollieren. Mit verwaltetem Code kann SQL Server hingegen erkennen, dass ein bestimmter Thread längere Zeit nicht aktiv war, und dann die Ausführung des Tasks erzwingen, damit andere Arbeit geplant werden kann. Infolgedessen kann mit verwaltetem Code eine bessere Skalierbarkeit und Systemressourcenverwendung erreicht werden.
Durch verwalteten Code können möglicherweise zusätzliche Kosten für die Aufrechterhaltung der Ausführungsumgebung sowie die Durchführung von Sicherheitsüberprüfungen entstehen. Das ist beispielsweise dann der Fall, wenn die Ausführung in SQL Server erfolgt und zahlreiche Übergänge von verwaltetem zu systemeigenem Code erforderlich sind (denn SQL Server muss für threadspezifische Einstellungen beim Übergang in systemeigenen Code und zurück zusätzlichen Wartungsaufwand betreiben). Folglich können erweiterte gespeicherte Prozeduren die Leistung im Vergleich zu verwaltetem Code, der in SQL Server ausgeführt wird, unter Umständen deutlich erhöhen, nämlich in Situationen mit häufigen Übergängen zwischen verwaltetem und systemeigenem Code.
Hinweis |
---|
Es wird jedoch empfohlen, keine neuen erweiterten gespeicherten Prozeduren zu entwickeln, da dieses Feature veraltet ist. |
Systemeigene Serialisierung für benutzerdefinierte Typen
Benutzerdefinierte Typen (User-defined Types, UDTs) sind als Erweiterungsmechanismus für das Skalartypsystem gedacht. SQL Server implementiert das Serialisierungsformat Format.Native für UDTs. Während der Kompilierung wird die Struktur des Typs zur Generierung von MSIL geprüft, das für die betreffende Typdefinition angepasst wird.
Die systemeigene Serialisierung ist die Standardimplementierung für SQL Server. Die benutzerdefinierte Serialisierung ruft eine vom Typautor definierte Methode auf, um die Serialisierung vorzunehmen. Die Format.Native-Serialisierung sollte immer dann verwendet werden, wenn es möglich ist, da sie beste Leistung sicherstellt.
Normalisierung vergleichbarer UDTs
Relationale Vorgänge, z. B. die Sortierung und das Vergleichen von UDTs, verwenden direkt die binäre Darstellung des Werts. Zu diesem Zweck wird eine normalisierte (binär sortierte) Darstellung des UDT-Zustands auf Festplatte gespeichert.
Normalisierungen bieten zwei Vorteile: Erstens verringern sie den Arbeitsaufwand für den Vergleich erheblich, indem sie die Konstruktion der Typinstanz und die Kosten für den Methodenaufruf vermeiden. Zweitens erstellen sie eine binäre Domäne für den UDT und ermöglichen so die Konstruktion von Histogrammen, Indizes und Histogrammen für Werte des Typs. Daher haben normalisierte UDTs ein Leistungsprofil, das dem von systemeigenen integrierten Typen sehr ähnlich ist, wenn es sich um Vorgänge handelt, die keinen Methodenaufruf erfordern.
Skalierbare Speicherverwendung
Damit die verwaltete Speicherbereinigung in SQL Server problemlos durchgeführt und skaliert werden kann, vermeiden Sie einzelne große Zuteilungen. Zuteilungen, die größer als 88 Kilobytes (KB) sind, werden im Objektheap für große Objekte positioniert. Das führt dazu, dass die automatische Speicherbereinigung schlechter ausgeführt und skaliert wird als bei vielen kleinen Zuteilungen. Wenn Sie beispielsweise ein großes mehrdimensionales Array zuteilen müssen, empfiehlt es sich, ein verzweigtes Array zuzuteilen.