CLR-Integrationsarchitektur: Von CLR gehostete Umgebung

Gilt für:SQL ServerAzure SQL Managed Instance

SQL Server Integration mit der .NET Framework Common Language Runtime (CLR) ermöglicht Es Datenbankprogrammierern, Sprachen wie Visual C#, Visual Basic .NET und Visual C++ zu verwenden. 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), Codeüberprü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 als Laufzeitumgebungen in der Art und Weise, wie sie Mit Arbeitsspeicher, Threads und Synchronisierung umgehen. 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 werden, 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 Intermediate Language (MSIL) enthält, und ein Manifest, 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 Benutzercode in der von CLR gehosteten Umgebung in SQL Server (clR-Integration genannt) 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 die CLR verfügen über unterschiedliche interne Modelle für die Planung und Speicherverwaltung. SQL Server unterstützt ein kooperatives, nicht präemptives Threadingmodell, bei dem die Threads die Ausführung freiwillig in regelmäßigen Abständen 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 Betriebssystemthreadinggrundtypen direkt aufrufen kann, lässt er sich nicht gut in den SQL Server Aufgabenplaner integrieren und kann die Skalierbarkeit des Systems beeinträchtigen. Die CLR unterscheidet nicht zwischen virtuellem und physischem Arbeitsspeicher, aber SQL Server verwaltet physischen Arbeitsspeicher direkt und muss physischen Arbeitsspeicher innerhalb eines konfigurierbaren Grenzwerts 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 beim Zugriff auf Datenbankobjekte wie Tabellen und Spalten SQL Server Authentifizierungs- und Autorisierungsregeln 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 solche Ressourcen bereitstellen. Das System muss eine sichere Möglichkeit für den Benutzercode für den Zugriff auf Computerressourcen außerhalb des Datenbank-Engine-Prozesses bieten. Weitere Informationen finden Sie unter CLR Integration Security.

Leistung

Verwalteter Benutzercode, der in der 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 aus verwaltetem Benutzercode ist nicht so schnell wie native Transact-SQL. Weitere Informationen finden Sie unter Leistung der 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 Integration Security.

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 Aufrufkette über eine Berechtigung verfügt.

Beachten Sie, dass die Möglichkeit, verwalteten Code dynamisch mithilfe der Reflection.Emit-API zu generieren, innerhalb 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 HostSchutzattribute und CLR-Integrationsprogrammierung.

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 sie 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 dieselben Grundtypen, die der Rest der SQL Server-Engine 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 Vorgehensweise 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, können SQL Server Threads planen, die andere Datenbanktasks ausführen, die nicht die CLR betreffen. Dadurch können 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 präemptiv ausgeführt. Der SQL Server Scheduler hat die Möglichkeit, Threads zu erkennen und zu beenden, die über einen längeren Zeitraum nicht zurückgegeben wurden. Die Möglichkeit, CLR-Threads mit SQL Server Threads zu verbinden, impliziert, dass der SQL Server Scheduler "runaway"-Threads in der CLR identifizieren und deren 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 andauernder verwalteter Code automatisch generiert wird, und in einigen Situationen, in denen dies nicht der Fehler ist. In den folgenden Situationen wird verwalteter Code mit langer Ausführungszeit automatisch generiert:

  • Wenn der Code das SQL-Betriebssystem aufruft (z. B. zum Abfragen von Daten)
  • Wenn genügend Arbeitsspeicher zum Auslösen der Garbage Collection zugewiesen ist
  • Wenn der Code durch Aufrufen von Betriebssystemfunktionen in den präemptiven Modus wechselt

Code, der keine der oben genannten Aktionen ausführt, z. B. enge Schleifen, die nur Berechnungen enthalten, gibt nicht automatisch den Scheduler zurück, was zu langen Wartezeiten für andere Workloads im System führen kann. In diesen Situationen ist es Aufgabe des Entwicklers, explizit die System.Thread.Sleep()-Funktion des .NET Framework aufzurufen oder explizit den präemtiven Modus mit System.Thread.BeginThreadAffinity() in allen Codeabschnitten zu betreten, die voraussichtlich lang sind. In den folgenden Codebeispielen wird gezeigt, wie sie mithilfe jeder dieser Methoden manuell ergibt.

// 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 Zuweisen und Aufheben der Zuweisung des Arbeitsspeichers auf. Da der von der CLR verwendete Arbeitsspeicher in der Gesamtspeicherauslastung des Systems berücksichtigt wird, können SQL Server die konfigurierten Speichergrenzwerte einhalten und sicherstellen, dass clR und SQL Server nicht miteinander um Arbeitsspeicher konkurrieren. SQL Server können auch CLR-Speicheranforderungen ablehnen, wenn der Systemarbeitsspeicher eingeschränkt ist, und die CLR auffordern, die Arbeitsspeicherauslastung zu reduzieren, 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.

Wenn sie in SQL Server gehostet werden, werden solche Threadabbrüche wie folgt behandelt: Die CLR erkennt jeden freigegebenen Zustand in der Anwendungsdomäne, in dem der Threadabbruch auftritt. Die CLR erkennt dies, indem sie überprüft, 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 eines freigegebenen Zustands die Auswirkungen solcher kritischer Ausnahmen auf Benutzersitzungen erweitern kann, die nicht die Ausnahme auslösen, haben SQL Server und die CLR Schritte unternommen, um die Wahrscheinlichkeit des freigegebenen Zustands zu verringern. Weitere Informationen hierzu finden Sie in der .NET Framework- Dokumentation.

Sicherheit: Berechtigungssätze

mit SQL Server können Benutzer die Zuverlässigkeits- und Sicherheitsanforderungen für code angeben, 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 Ja Ja Keine Einschränkungen
Überprüfbarkeit erforderlich Ja Ja Nein
Aufrufbarkeit von systemeigenem Code Nein 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 basierend auf dem in SQL Server Katalogen gespeicherten Berechtigungssatz einen der drei Berechtigungssätze 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 keine Verwendung des Zustands erfordern, der über mehrere Aufrufe hinweg gehalten wird, oder die Freigabe des Zustands über mehrere Benutzersitzungen hinweg. 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.

Angesichts dieser Überlegungen wird die Verwendung statischer Variablen und statischer Datenmember von Klassen, die in SQL Server verwendet werden, abgeraten. Für 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 statische Datenmember und Variablen verwendet werden.

SQL Server lässt auch Keine Aufrufe von .NET Framework APIs zu, die mit den Hostschutzattributen SharedState, Synchronization und ExternalProcessMgmt kommentiert sind. Dadurch wird verhindert, dass SAFE- und EXTERNAL_ACCESS assemblys APIs aufrufen, die den Freigabestatus ermöglichen, eine 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