CLR-Integrationsarchitektur – Leistung

Gilt für:SQL ServerAzure SQL Managed Instance

In diesem Thema werden einige der Entwurfsoptionen erläutert, die die Leistung von Microsoft SQL Server Integration in die 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 MSIL-Stub (Microsoft Intermediate Language) generiert. Dieser Stub enthält Code, um die Routineparameter aus SQL Server in die CLR zu marshallen, die Funktion aufzurufen und das Ergebnis zurückzugeben. Dieser "Verbindungscode" basiert auf dem Parametertyp und der Parameterrichtung (IN, OUT oder Verweis).

Der Gluecode ermöglicht typspezifische Optimierungen und stellt eine effiziente Durchsetzung SQL Server Semantik sicher, z. B. NULL-Zulässigkeit, einschränkende Facetten, Wert-By-Value und Standard-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 in nativen Code kompiliert und für die jeweilige Hardwarearchitektur optimiert, auf der SQL Server ausgeführt wird, indem die JIT-Kompilierungsdienste (Just-In-Time) der CLR verwendet werden. Die JIT-Dienste werden auf Methodenebene aufgerufen und ermöglichen es der SQL Server Hostingumgebung, eine einzelne Kompilierungseinheit zu erstellen, die sich über SQL Server- und CLR-Ausführung erstreckt. 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 der CLR zu minimieren, verfügen Anweisungen, die alle verwalteten Aufrufe enthalten, über einen Startschritt, um die Zielanwendungsdomäne zu identifizieren. Dieser Identifizierungsschritt reduziert die Kosten für den Übergang der einzelnen Zeilen.

Überlegungen zur Leistung

Im Folgenden werden leistungsspezifische Überlegungen zur CLR-Integration in SQL Server zusammengefasst. Ausführlichere Informationen finden Sie unter "Using CLR Integration in SQL Server 2005" (Verwenden der CLR-Integration in SQL Server 2005) auf der MSDN-Website. Allgemeine Informationen zur Leistung von verwaltetem Code finden Sie unter Verbessern der Leistung und Skalierbarkeit von .NET-Anwendungen auf der MSDN-Website.

Benutzerdefinierte Funktionen

CLR-Funktionen profitieren von einem schnelleren Aufrufpfad als der von benutzerdefinierten Transact-SQL-Funktionen. Darüber hinaus hat verwalteter Code einen entscheidenden Leistungsvorteil gegenüber Transact-SQL in Bezug auf prozeduralen Code, Berechnungen und Zeichenfolgenbearbeitung. Rechenintensive CLR-Funktionen, die keinen Datenzugriff ausführen, sollten bevorzugt in verwaltetem Code geschrieben werden. Transact-SQL-Funktionen führen jedoch den Datenzugriff effizienter aus als die CLR-Integration.

Benutzerdefinierte Aggregate

Verwalteter Code ist deutlich leistungsfähiger als die cursorbasierte Aggregation. Verwalteter Code funktioniert im Allgemeinen 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. Die Integration der CLR in SQL Server führt einen neuen Erweiterbarkeitsmechanismus ein, der als Streamingtabellenwertfunktion (STVF) bezeichnet wird. Verwaltete STVF sind leistungsfähiger als vergleichbare Implementierungen mit erweiterten gespeicherten Prozeduren.

STVFs sind verwaltete Funktionen, die eine IEnumerable-Schnittstelle zurückgeben. IEnumerable verfügt über Methoden zum Navigieren im resultset, das vom STVF zurückgegeben wird. Wenn die STVF aufgerufen wird, wird die zurückgegebene IEnumerable direkt mit dem Abfrageplan verbunden. Der Abfrageplan ruft IEnumerable-Methoden auf, wenn Zeilen abgerufen werden müssen. 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 durchlaufen müssen, die einfacher als Array ausgedrückt werden können, kann verwalteter Code mit erheblichen Leistungssteigerungen verwendet werden.

Zeichenfolgendaten

SQL Server Zeichendaten, 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 (9.x) eingeführt wurden, während die ODS-APIs nicht erweitert wurden, um die neuen Datentypen zu unterstützen.

Mit verwaltetem Code verwaltet SQL Server die Verwendung von Ressourcen wie Arbeitsspeicher, Threads und Synchronisierung. Dies liegt daran, dass die verwalteten APIs, die diese Ressourcen verfügbar machen, zusätzlich zum SQL Server Ressourcen-Manager implementiert werden. Umgekehrt hat SQL Server keine Ansicht oder Kontrolle über die Ressourcennutzung der erweiterten gespeicherten Prozedur. Wenn beispielsweise eine erweiterte gespeicherte Prozedur zu viel CPU- oder Arbeitsspeicherressourcen verbraucht, gibt es keine Möglichkeit, dies mit SQL Server zu erkennen oder zu steuern. Mit verwaltetem Code können SQL Server jedoch erkennen, dass ein bestimmter Thread über einen längeren Zeitraum nicht zurückgegeben wurde, und dann erzwingen, dass die Aufgabe ergibt, damit andere Arbeiten geplant werden können. 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. Dies ist beispielsweise der Fall, wenn die Ausführung innerhalb SQL Server ausgeführt wird und zahlreiche Übergänge von verwaltetem zu nativem Code erforderlich sind (da SQL Server zusätzliche Wartung für threadspezifische Einstellungen durchführen muss, wenn Sie zu nativem Code und zurück wechseln). Daher können erweiterte gespeicherte Prozeduren verwalteten Code, der in SQL Server ausgeführt wird, erheblich übertreffen, wenn es häufig Übergänge zwischen verwaltetem und nativem Code gibt.

Hinweis

Es wird jedoch empfohlen, keine neuen erweiterten gespeicherten Prozeduren zu entwickeln, da diese Funktion veraltet ist.

Systemeigene Serialisierung für benutzerdefinierte Typen

Benutzerdefinierte Typen (UDTs) wurden als Erweiterungsmechanismus für das Skalartypsystem entworfen. SQL Server implementiert ein Serialisierungsformat für UDTs namens Format.Native. Während der Kompilierung wird die Struktur des Typs zur Generierung von MSIL geprüft, das für die betreffende Typdefinition angepasst wird.

Die native Serialisierung ist die Standardimplementierung für SQL Server. Die benutzerdefinierte Serialisierung ruft eine Methode auf, die vom Typautor für die Durchführung der Serialisierung definiert wurde. Die Format.Native-Serialisierung sollte nach Möglichkeit verwendet werden, um eine optimale Leistung zu erzielen.

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 Garbage Collection in SQL Server gut funktioniert und skaliert werden kann, vermeiden Sie eine große, einzelne Zuordnung. 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.

Weitere Informationen

Benutzerdefinierte CLR-Typen