Teilen über


CLR-Integrationsarchitektur: Von CLR gehostete Umgebung

Gilt für: SQL Server Azure SQL verwaltete Instanz

Die SQL Server-Integration in die Common Language Runtime (CLR) von .NET Framework ermöglicht Datenbankprogrammierern die Verwendung von Sprachen wie Visual C#, Visual Basic .NET und Visual C++. Funktionen, gespeicherte Prozeduren, Trigger, Datentypen und Aggregate gehören zu den Arten von Geschäftslogik, die Programmierer in diesen Sprachen schreiben können.

Die CLR verfügt über garbage-collection memory, preemptive Threading, Metadatendienste (Typreflektion), Codeprüfbarkeit und Codezugriffssicherheit. Die CLR verwendet Metadaten zum Suchen und Laden von Klassen, Anordnen von Instanzen im Speicher, Auflösen von Methodenaufrufen, Generieren von systemeigenem Code, Erzwingen von Sicherheit und zum Festlegen von Begrenzungen im Laufzeitkontext.

Die CLR und SQL Server unterscheiden sich in der Art und Weise, wie sie Arbeitsspeicher, Threads und Synchronisierung verarbeiten, als Laufzeitumgebungen. In diesem Artikel wird beschrieben, wie diese beiden Laufzeiten integriert werden, damit alle Systemressourcen einheitlich verwaltet werden. In diesem Artikel wird auch erläutert, wie CLR-Codezugriffssicherheit (CAS) und SQL Server-Sicherheit integriert sind, um eine zuverlässige und sichere Ausführungsumgebung für Benutzercode bereitzustellen.

Grundlegende Konzepte der CLR-Architektur

In .NET Framework schreibt ein Programmierer in einer Hochsprache, die eine Klasse implementiert, die die Struktur (z. B. die Felder oder Eigenschaften der Klasse) und Methoden definiert. Einige dieser Methoden können statische Funktionen sein. Die Kompilierung des Programms erzeugt eine Datei namens "Assembly", die den kompilierten Code in der Microsoft-Zwischensprache (MSIL) und ein Manifest enthält, das alle Verweise auf abhängige Assemblys enthält.

Hinweis

Assemblys sind ein wichtiges Element in der Architektur der CLR. Sie sind die Einheiten für Verpackung, Bereitstellung und Versionsverwaltung des Anwendungscodes in .NET Framework. Durch die Verwendung von Assemblys können Sie Anwendungscode in der Datenbank verfügbar machen und eine einheitliche Methode zur Verwaltung, Sicherung und Wiederherstellung kompletter Datenbankanwendungen bereitstellen.

Das Assemblymanifest enthält Metadaten über die Assembly, die sämtliche Strukturen, Felder, Eigenschaften, Klassen, Vererbungsbeziehungen, Funktionen und Methoden beschreiben, die im Programm definiert sind. Das Manifest legt die Identität der Assembly fest, gibt die Dateien an, aus denen die Assemblyimplementierung besteht, gibt die Typen und Ressourcen an, aus denen die Assembly besteht, legt die Abhängigkeiten von anderen Assemblys für die Kompilierungszeit einzeln fest und gibt den Berechtigungssatz an, der für die ordnungsgemäße Ausführung der Assembly erforderlich ist. Diese Informationen werden zur Laufzeit verwendet, um Verweise aufzulösen, Versionsbindungsrichtlinien zu erzwingen und die Integrität geladener Assemblys zu validieren.

.NET Framework unterstützt benutzerdefinierte Attribute zum Hinzufügen von Anmerkungen zu Klassen, Eigenschaften, Funktionen und Methoden in Form von zusätzlichen Informationen, die von der Anwendung in Metadaten erfasst werden können. Alle .NET Framework-Compiler verwenden diese Anmerkungen ohne Auslegung und speichern sie als Assemblymetadaten. Diese Anmerkungen können auf die gleiche Weise wie beliebige andere Metadaten untersucht werden.

Verwalteter Code ist MSIL-Code, der in der CLR anstatt direkt vom Betriebssystem ausgeführt wird. Anwendungen mit verwaltetem Code verfügen über CLR-Dienste, z. B. automatische Garbage Collection, Typüberprüfung zur Laufzeit, Sicherheitsunterstützung usw. Diese Dienste helfen dabei, ein einheitliches plattform- und sprachunabhängiges Verhalten von Anwendungen mit verwaltetem Code bereitzustellen.

Entwurfsziele der CLR-Integration

Wenn Der Benutzercode in der von CLR gehosteten Umgebung in SQL Server (als CLR-Integration bezeichnet) ausgeführt wird, gelten die folgenden Entwurfsziele:

Zuverlässigkeit (Sicherheit)

Vom Benutzercode sollten keine Vorgänge ausgeführt werden dürfen, die die Integrität des Prozesses der Datenbank-Engine gefährden, z. B. die Anzeige eines Meldungsfelds, in dem eine Benutzerantwort verlangt wird, oder in dem der Prozess beendet wird. Benutzercode sollte nicht in der Lage sein, Arbeitsspeicherpuffer der Datenbank-Engine oder interne Datenstrukturen zu überschreiben.

Skalierbarkeit

SQL Server und CLR verfügen über unterschiedliche interne Modelle für die Planung und Speicherverwaltung. SQL Server unterstützt ein kooperatives, nicht preemptives Threadingmodell, in dem die Threads die Ausführung freiwillig regelmäßig zurückgeben oder wenn sie auf Sperren oder E/A warten. Die CLR unterstützt ein präemptives Threadingmodell. Wenn Benutzercode, der in SQL Server ausgeführt wird, die Grundtypen des Betriebssystemthreadings direkt aufrufen kann, ist er nicht gut in den SQL Server-Aufgabenplaner integriert und kann die Skalierbarkeit des Systems beeinträchtigen. Die CLR unterscheidet nicht zwischen virtuellem und physischem Arbeitsspeicher, aber SQL Server verwaltet den physischen Speicher direkt und ist erforderlich, um physischen Speicher innerhalb eines konfigurierbaren Grenzwerts zu verwenden.

Die unterschiedlichen Modelle für Threading, Planung und Arbeitsspeicherverwaltung stellen eine Integrationsherausforderung für ein relationales Datenbankverwaltungssystem (RDBMS) dar, das durch Skalierung Tausende von gleichzeitigen Benutzersitzungen unterstützt. Durch die Architektur sollte sichergestellt werden, dass die Skalierbarkeit des Systems nicht durch Benutzercode beeinträchtigt wird, der APIs (Application Programming Interfaces, Schnittstellen zur Anwendungsprogrammierung) für Threading-, Arbeitsspeicher- und Synchronisierungsgrundelemente direkt aufruft.

Sicherheit

Benutzercode, der in der Datenbank ausgeführt wird, muss SQL Server-Authentifizierungs- und Autorisierungsregeln beim Zugriff auf Datenbankobjekte wie Tabellen und Spalten befolgen. Darüber hinaus sollten Datenbankadministratoren in der Lage sein, den Zugriff auf Ressourcen des Betriebssystems, wie Dateien und Netzwerkzugriff, vom Benutzercode aus zu steuern, der in der Datenbank ausgeführt wird. Diese Vorgehensweise wird wichtig, da verwaltete Programmiersprachen (im Gegensatz zu nicht verwalteten Sprachen wie Transact-SQL) APIs für den Zugriff auf diese Ressourcen bereitstellen. Das System muss eine sichere Möglichkeit für den Benutzercode bereitstellen, um außerhalb des Datenbank-Engine Prozesses auf Computerressourcen zuzugreifen. Weitere Informationen finden Sie unter CLR Integration Security.

Leistung

Verwalteter Benutzercode, der im Datenbank-Engine ausgeführt wird, sollte eine Berechnungsleistung aufweisen, die mit demselben Code vergleichbar ist, der außerhalb des Servers ausgeführt wird. Der Datenbankzugriff über verwalteten Benutzercode ist nicht so schnell wie systemeigene Transact-SQL. Weitere Informationen finden Sie unter Performance of CLR Integration.

CLR Services

Die CLR bietet eine Reihe von Diensten, um die Entwurfsziele der CLR-Integration mit SQL Server zu erreichen.

Typsicherheitsüberprüfung

Als typsicherer Code wird Code bezeichnet, der nur auf genau definierte Weise auf Arbeitsspeicherstrukturen zugreift. Bei typsicherem Code ist z. B. bei einem gültigen Objektverweis der Speicherzugriff an festen Offsets möglich, die tatsächlichen Feldmembern entsprechen. Wenn der Code jedoch auf den Speicher an beliebigen Offsets innerhalb oder außerhalb des Gültigkeitsbereichs des Speichers zugreift, der zu dem Objekt gehört, ist er nicht typsicher. Wenn Assemblys in die CLR geladen werden, bevor die MSIL mithilfe der JIT-Kompilierung (Just-In-Time) kompiliert wurde, führt die Laufzeit eine Überprüfungsphase aus, die den Code hinsichtlich seiner Typsicherheit untersucht. Code, der diese Überprüfung erfolgreich besteht, wird nachweisbar typsicherer Code genannt.

Anwendungsdomänen

Die CLR unterstützt die Definition von Anwendungsdomänen als Ausführungszonen in einem Hostprozess, bei dem verwaltete Codeassemblys geladen und ausgeführt werden können. Die Anwendungsdomänengrenze bietet Isolierung zwischen Assemblys. Die Assemblys werden hinsichtlich der Sichtbarkeit von statischen Variablen und Datenelementen und der Möglichkeit, Code dynamisch aufzurufen, isoliert. Anwendungsdomänen sind auch der Mechanismus zum Laden und Entladen von Code. Code kann aus dem Arbeitsspeicher durch das Entladen der Anwendungsdomäne entfernt werden. Weitere Informationen finden Sie unter Anwendungsdomänen und CLR-Integrationssicherheit.

Codezugriffssicherheit (Code Access Security, CAS)

Das CLR-Sicherheitssystem bietet eine Methode zum Steuern der Vorgangstypen, die von verwaltetem Code ausgeführt werden können, indem dem Code Berechtigungen zugewiesen werden. Codezugriffsberechtigungen werden auf der Grundlage der Identität des Codes (z. B. die Signatur der Assembly oder die Quelle des Codes) zugewiesen.

Die CLR sorgt für eine Richtlinie, die auf dem gesamten Computer gilt und vom Computeradministrator festgelegt werden kann. Diese Richtlinie definiert die Berechtigungen für den gesamten verwalteten Code, der auf dem Computer ausgeführt wird. Darüber hinaus gibt es eine Sicherheitsrichtlinie auf Hostebene, die von Hosts wie SQL Server verwendet werden kann, um zusätzliche Einschränkungen für verwalteten Code anzugeben.

Wenn eine verwaltete API in .NET Framework Operationen für Ressourcen verfügbar macht, die durch eine Codezugriffsberechtigung geschützt sind, fordert die API vor dem Zugriff auf die Ressource diese Berechtigung an. Diese Anforderung löst im CLR-Sicherheitssystem eine umfangreiche Überprüfung jeder Codeeinheit (Assembly) in der Aufrufliste aus. Der Zugriff auf die Ressource wird nur gewährt, wenn die gesamte Anrufkette über die Berechtigung verfügt.

Beachten Sie, dass die Möglichkeit zum dynamischen Generieren von verwaltetem Code mithilfe der Reflection.Emit-API in der CLR-gehosteten Umgebung in SQL Server nicht unterstützt wird. Solcher Code hätte die CAS-Berechtigungen zum Ausführen nicht und würde deshalb zur Laufzeit fehlschlagen. Weitere Informationen finden Sie unter CLR Integration Code Access Security.

Host protection Attributes (HPAs)

Die CLR stellt einen Mechanismus zur Verfügung, um verwaltete APIs, die Teil von .NET Framework sind, mit Anmerkungen in Form von bestimmten Attributen zu versehen, die für einen Host der CLR interessant sein können. Beispiele für solche Attribute:

  • SharedState - gibt an, ob die API die Fähigkeit zur Verfügung stellt, einen Freigabezustand (z. B. statische Klassenfelder) zu erstellen oder zu verwalten.

  • Synchronization - gibt an, ob die API die Fähigkeit zur Verfügung stellt, die Synchronisierung zwischen Threads auszuführen.

  • ExternalProcessMgmt - gibt an, ob die API eine Möglichkeit zur Kontrolle des Hostprozesses zur Verfügung stellt.

Anhand dieser Attribute kann der Host eine Liste mit Hostschutzattributen festlegen, wie das SharedState-Attribut, die in der gehosteten Umgebung nicht zugelassen werden. In diesem Fall weist die CLR Versuche des Benutzercodes zurück, APIs aufzurufen, die mit nicht zugelassenen Hostschutzattributen aus der Liste versehen sind. Weitere Informationen finden Sie unter Host Protection Attributes and CLR Integration Programming.

Zusammenarbeit von SQL Server und der CLR

In diesem Abschnitt wird erläutert, wie SQL Server die Threading-, Planungs-, Synchronisierungs- und Speicherverwaltungsmodelle von SQL Server und der CLR integriert. Genauer wird in diesem Abschnitt auf die Integration im Hinblick auf Skalierbarkeit, Zuverlässigkeit und Sicherheit eingegangen. SQL Server fungiert im Wesentlichen als Betriebssystem für die CLR, wenn es in SQL Server gehostet wird. Die CLR ruft Routinen auf niedriger Ebene auf, die von SQL Server für Threading, Planung, Synchronisierung und Speicherverwaltung implementiert werden. Diese Routinen sind die gleichen Grundtypen, die der Rest des SQL Server-Moduls verwendet. Dieser Ansatz bietet mehrere Vorteile für Skalierbarkeit, Zuverlässigkeit und Sicherheit.

Skalierbarkeit: Allgemeines Threading, Planung und Synchronisierung

CLR ruft SQL Server-APIs zum Erstellen von Threads auf, sowohl für die Ausführung von Benutzercode als auch für die eigene interne Verwendung. Um zwischen mehreren Threads zu synchronisieren, ruft die CLR SQL Server-Synchronisierungsobjekte auf. Diese Übung ermöglicht es dem SQL Server-Scheduler, andere Aufgaben zu planen, wenn ein Thread auf ein Synchronisierungsobjekt wartet. Wenn die CLR beispielsweise die Garbage Collection initiiert, warten alle zugehörigen Threads auf die Fertigstellung der Garbage Collection. Da die CLR-Threads und die Synchronisierungsobjekte, auf die sie warten, dem SQL Server-Scheduler bekannt sind, kann SQL Server Threads planen, die andere Datenbankaufgaben ausführen, die nicht mit der CLR verbunden sind. Dadurch kann SQL Server auch Deadlocks erkennen, die Sperren von CLR-Synchronisierungsobjekten umfassen und herkömmliche Techniken zum Entfernen von Deadlocks verwenden.

Verwalteter Code wird in SQL Server vorab ausgeführt. Der SQL Server-Scheduler verfügt über die Möglichkeit, Threads zu erkennen und zu beenden, die für einen erheblichen Zeitraum nicht zurückgegeben wurden. Die Möglichkeit, CLR-Threads mit SQL Server-Threads zu verbinden, bedeutet, dass der SQL Server-Scheduler "Runaway"-Threads in der CLR identifizieren und ihre Priorität verwalten kann. Solche Ausreißerthreads werden angehalten und in die Warteschlange zurückgestellt. Threads, die mehrfach als Ausreißerthreads identifiziert werden, dürfen für einen gewissen Zeitraum nicht ausgeführt werden, damit andere Arbeitsthreads ausgeführt werden können.

Es gibt einige Situationen, in denen lang ausgeführter verwalteter Code automatisch zurückgegeben wird, und in manchen Situationen, in denen der Code nicht ausgeführt wird. In den folgenden Situationen führt der lang ausgeführte verwaltete Code automatisch zu einer automatischen Generierung:

  • Wenn der Code das SQL-Betriebssystem aufruft (z. B. zum Abfragen von Daten)
  • Wenn genügend Arbeitsspeicher zugewiesen ist, um die Garbage Collection auszulösen
  • Wenn der Code in den präemptiven Modus wechselt, indem Betriebssystemfunktionen aufgerufen werden

Code, der keines der obigen Aktionen vorgibt, z. B. enge Schleifen, die nur Berechnungen enthalten, liefert nicht automatisch den Zeitplan, was zu langen Wartezeiten für andere Workloads im System führen kann. In diesen Situationen muss der Entwickler explizit durch Aufrufen der System.Thread.Sleep()-Funktion von .NET Framework oder durch explizite Eingabe des präemtiven Modus mit System.Thread.BeginThreadAffinity() in allen Codeabschnitten, die voraussichtlich lang ausgeführt werden, explizit zurückgeben. Die folgenden Codebeispiele zeigen, wie Sie mithilfe jeder dieser Methoden manuell ausbeuten können.

// Example 1: Manually yield to SOS scheduler.
for (int i = 0; i < Int32.MaxValue; i++)
{
 // *Code that does compute-heavy operation, and does not call into
 // any OS functions.*

 // Manually yield to the scheduler regularly after every few cycles.
 if (i % 1000 == 0)
 {
   Thread.Sleep(0);
 }
}
// Example 2: Use ThreadAffinity to run preemptively.
// Within BeginThreadAffinity/EndThreadAffinity the CLR code runs in preemptive mode.
Thread.BeginThreadAffinity();
for (int i = 0; i < Int32.MaxValue; i++)
{
  // *Code that does compute-heavy operation, and does not call into
  // any OS functions.*
}
Thread.EndThreadAffinity();
Skalierbarkeit: Allgemeine Arbeitsspeicherverwaltung

Die CLR ruft SQL Server-Grundtypen zum Zuordnen und Aufheben der Zuordnung des Arbeitsspeichers auf. Da der vom CLR verwendete Arbeitsspeicher in der Gesamtspeicherauslastung des Systems berücksichtigt wird, kann SQL Server innerhalb der konfigurierten Speichergrenzen bleiben und sicherstellen, dass die CLR und SQL Server nicht miteinander konkurrieren. SQL Server kann auch CLR-Speicheranforderungen ablehnen, wenn der Systemspeicher eingeschränkt ist, und clR bitten, die Speichernutzung zu verringern, wenn andere Aufgaben Arbeitsspeicher benötigen.

Zuverlässigkeit: Anwendungsdomänen und nicht behebbare Ausnahmen

Wenn in verwaltetem Code in .NET Framework-APIs schwerwiegende Ausnahmen auftreten, wie nicht genügend Arbeitsspeicher oder Stapelüberlauf, ist es nicht immer möglich, diese Fehler zu beheben und konsistente und korrekte Semantik für die Implementierung zu gewährleisten. Diese APIs lösen als Reaktion auf diese Fehler eine Threadabbruchausnahme aus.

Bei der Ausführung in SQL Server werden solche Threadabbrüche wie folgt behandelt: Die CLR erkennt jeden freigegebenen Zustand in der Anwendungsdomäne, in der der Thread abgebrochen wird. Die CLR erkennt dies, indem überprüft wird, ob Synchronisierungsobjekte vorhanden sind. Wenn es einen Freigabezustand in der Anwendungsdomäne gibt, dann wird die Anwendungsdomäne selbst entladen. Die Entladung aus der Anwendungsdomäne beendet Datenbanktransaktionen, die gerade in dieser Anwendungsdomäne ausgeführt werden. Da das Vorhandensein des freigegebenen Zustands die Auswirkungen solcher kritischer Ausnahmen auf andere Benutzersitzungen als die ausnahme auslösen kann, haben SQL Server und die CLR Schritte unternommen, um die Wahrscheinlichkeit des freigegebenen Zustands zu verringern. Weitere Informationen finden Sie in der .NET Framework-Dokumentation.

Sicherheit: Berechtigungssätze

SQL Server ermöglicht Benutzern die Angabe der Zuverlässigkeits- und Sicherheitsanforderungen für code, der in der Datenbank bereitgestellt wird. Wenn Assemblys in die Datenbank hochgeladen werden, kann der Autor der Assembly einen von drei Berechtigungssätzen für diese Assembly angeben: SAFE, EXTERNAL_ACCESS und UNSAFE.

Funktionalität SAFE EXTERNAL_ACCESS UNSAFE
Codezugriffssicherheit Nur ausführen Ausführen + Zugriff auf externe Ressourcen Nicht eingeschränkt
Beschränkungen des Programmiermodells Yes Yes Keine Einschränkungen
Überprüfbarkeit erforderlich Yes Ja Nein
Aufrufbarkeit von systemeigenem Code No Nein Ja

SAFE ist der zuverlässigste und sicherste Modus, der mit Einschränkungen hinsichtlich des zulässigen Programmiermodells einhergeht. Assemblys der Stufe SAFE verfügen über ausreichende Berechtigungen für die Ausführung, die Durchführung von Berechnungen und den Zugriff auf die lokale Datenbank. Assemblys der Stufe SAFE müssen nachweislich typsicher sein und dürfen keinen nicht verwalteten Code aufrufen.

UNSAFE ist für hoch vertrauenswürdigen Code vorgesehen, der nur von Datenbankadministratoren erstellt werden kann. Dieser vertrauenswürdige Code verfügt über keine Codezugriffs-Sicherheitsbeschränkungen und kann nicht verwalteten (systemeigenen) Code aufrufen.

EXTERNAL_ACCESS stellt eine mittlere Sicherheitsebene dar, bei der Code auf Ressourcen außerhalb der Datenbank zugreifen kann, die aber dennoch so zuverlässig wie SAFE ist.

SQL Server verwendet die CAS-Richtlinienebene auf Hostebene, um eine Hostrichtlinie einzurichten, die einen der drei Berechtigungssätze basierend auf dem in SQL Server-Katalogen gespeicherten Berechtigungssatz gewährt. Verwaltetem Code, der in der Datenbank ausgeführt wird, wird immer einer dieser Codezugriffsberechtigungssätze abgerufen.

Einschränkungen des Programmiermodells

Das Programmiermodell für verwalteten Code in SQL Server umfasst das Schreiben von Funktionen, Prozeduren und Typen, die in der Regel nicht die Verwendung des Zustands über mehrere Aufrufe oder die Freigabe des Zustands über mehrere Benutzersitzungen hinweg erfordern. Darüber hinaus kann ein Freigabezustand, wie bereits erläutert, zu schwerwiegenden Ausnahmen führen, die die Skalierbarkeit und Zuverlässigkeit der Anwendung gefährden können.

In Anbetracht dieser Überlegungen wird die Verwendung statischer Variablen und statischer Datenmmber von Klassen, die in SQL Server verwendet werden, abgeraten. Bei SAFE- und EXTERNAL_ACCESS Assemblys untersucht SQL Server die Metadaten der Assembly zur CREATE ASSEMBLY-Zeit und schlägt bei der Erstellung solcher Assemblys fehl, wenn die Verwendung statischer Datenelemente und -variablen gefunden wird.

SQL Server verbietet auch Aufrufe von .NET Framework-APIs, die mit den Hostschutzattributen "SharedState", "Synchronization" und "ExternalProcessMgmt " versehen sind. Dadurch wird verhindert, dass SAFE- und EXTERNAL_ACCESS Assemblys APIs aufrufen, die den Freigabestatus aktivieren, die Synchronisierung durchführen und die Integrität des SQL Server-Prozesses beeinträchtigen. Weitere Informationen finden Sie unter EINSCHRÄNKUNGEN des CLR-Integrationsprogrammierungsmodells.

Weitere Informationen

Sicherheit der CLR-Integration
Leistungsfähigkeit der CLR-Integration