Freigeben über


CLR-Integrationsarchitektur – gehostete CLR-Umgebung

Gilt für:SQL ServerAzure SQL Managed Instance

Die SQL Server-Integration in die Common Language Runtime (CLR) von .NET Framework ermöglicht Datenbankprogrammierern die Verwendung von Sprachen wie 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 allgemeinen Zwischensprache (CIL) 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 des Verpackens, der Bereitstellung und der Versionsverwaltung von Anwendungscode 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 Kommentieren von Klassen, Eigenschaften, Funktionen und Methoden mit zusätzlichen Informationen, die die Anwendung in Metadaten erfassen kann. 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 wird in der CLR statt direkt vom Betriebssystem ausgeführt. 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.

Designziele 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)

Benutzercode darf keine Vorgänge ausführen, die die Integrität des Datenbankmodulprozesses gefährden, z. B. das Aufspringen eines Meldungsfelds, das eine Benutzerantwort anfordert oder den Prozess verlässt. Benutzercode sollte nicht in der Lage sein, Arbeitsspeicherpuffer des Datenbankmoduls 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 präemptives 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. Die Architektur sollte sicherstellen, dass die Skalierbarkeit des Systems nicht durch Benutzercode beeinträchtigt wird, der Anwendungsprogrammierschnittstellen (APPLICATION Programming Interfaces, APIs) für Threading, Arbeitsspeicher und Synchronisierungsgrundtypen 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-Integrationssicherheit.

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 Leistung der CLR-Integrationsarchitektur.

CLR-Dienste

Die CLR bietet mehrere Dienste, um die Entwurfsziele der CLR-Integration in 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 Arbeitsspeicher bei beliebigen Offsets innerhalb oder außerhalb des Speicherbereichs zugreift, der zum Objekt gehört, ist er nicht typsicher. Wenn Assemblys in der CLR geladen werden, bevor die CIL mit just-in-time(JIT)-Kompilierung kompiliert wird, führt die Laufzeit eine Überprüfungsphase durch, in der Code untersucht wird, um die Typsicherheit zu ermitteln. 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 im .NET Framework Vorgänge für Ressourcen verfügbar macht, die durch eine Codezugriffsberechtigung geschützt sind, fordert die API diese Berechtigung vor dem Zugriff auf die Ressource 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.

Die Möglichkeit, verwalteten Code dynamisch mithilfe der Reflection.Emit-API zu generieren, wird in der von CLR gehosteten Umgebung in SQL Server nicht unterstützt. Dieser Code würde nicht über die CAS-Berechtigungen zum Ausführen verfügen und würde daher zur Laufzeit fehlschlagen. Weitere Informationen finden Sie unter CLR Integration Code Access Security.

Hostschutzattribute (HPAs)

Die CLR bietet einen Mechanismus zum Kommentieren von verwalteten APIs, die Teil von .NET Framework sind, mit bestimmten Attributen, die für einen Host der CLR von Interesse sein könnten. Beispiele für solche Attribute:

  • SharedState, der angibt, ob die API die Möglichkeit zum Erstellen oder Verwalten des freigegebenen Zustands (z. B. statische Klassenfelder) verfügbar macht.

  • Synchronization, der angibt, ob die API die Möglichkeit zur Ausführung der Synchronisierung zwischen Threads verfügbar macht.

  • ExternalProcessMgmt, der angibt, ob die API eine Möglichkeit zum Steuern des Hostprozesses verfügbar macht.

Angesichts dieser Attribute kann der Host eine Liste von HPAs angeben, z. B. das SharedState-Attribut, das in der gehosteten Umgebung nicht zulässig sein sollte. 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 zwischen SQL Server und 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 wiederholt als Runaway-Threads identifiziert werden, dürfen nicht für einen bestimmten Zeitraum ausgeführt werden, damit andere ausführende Mitarbeiter ausgeführt werden können.

Es gibt einige Situationen, in denen lang ausgeführter verwalteter Code automatisch generiert wird, und in einigen Situationen, in denen er nicht funktioniert. In den folgenden Situationen liefert lang ausgeführter verwalteter Code automatisch Folgendes:

  • 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 keine dieser Aktionen tut, z. B. enge Schleifen, die nur Berechnung enthalten, liefern nicht automatisch den Zeitplan, was zu langen Wartezeiten für andere Arbeitslasten im System führen kann. In diesen Situationen muss der Entwickler explizit die System.Thread.Sleep()-Funktion von .NET Framework aufrufen oder den präemptiven Modus mit System.Thread.BeginThreadAffinity()explizit in alle Codeabschnitte eingeben, die voraussichtlich lang ausgeführt werden. Die folgenden Codebeispiele zeigen, wie Sie mithilfe jeder dieser Methoden manuell ausbeuten können.

Beispiele

Manuelles Zuordnen zum 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);
  }
}

Verwenden von ThreadAffinity, um vorab ausgeführt zu werden

In diesem Beispiel wird der CLR-Code im präemptiven Modus innerhalb von BeginThreadAffinity und EndThreadAffinityausgeführt.

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 Behandeln des Speichers 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 verwalteter Code in den .NET Framework-APIs auf kritische Ausnahmen wie out-of-memory oder stack overflow stößt, ist es nicht immer möglich, aus solchen Fehlern wiederherzustellen und konsistente und korrekte Semantik für ihre Implementierung sicherzustellen. 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 der freigegebene Zustand in der Anwendungsdomäne vorhanden ist, 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 auslösende Ausnahme erweitern kann, haben SQL Server und die CLR Schritte unternommen, um die Wahrscheinlichkeit des freigegebenen Zustands zu verringern. Weitere Informationen finden Sie unter .NET Framework.

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_ACCESSund UNSAFE.

Funktionalität SAFE EXTERNAL_ACCESS UNSAFE
Code Access Security Nur ausführen Ausführen + Zugriff auf externe Ressourcen Nicht eingeschränkt
Programming model restrictions Yes Yes Keine Einschränkungen
Verifiability requirement Yes Yes Nein
Ability to call native code Nein Nein Yes

SAFE ist der zuverlässigste und sicherste Modus mit zugehörigen Einschränkungen im Hinblick auf das zulässige Programmiermodell. SAFE Assemblys verfügen über ausreichende Berechtigungen zum Ausführen, Ausführen von Berechnungen und Zugriff auf die lokale Datenbank. SAFE Assemblys müssen sicher typsicher sein und dürfen keinen nicht verwalteten Code aufrufen.

UNSAFE gilt für vertrauenswürdigen Code, 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 bietet eine Zwischensicherheitsoption, sodass Code auf Ressourcen außerhalb der Datenbank zugreifen kann, aber dennoch die Zuverlässigkeitsgarantien für SAFE.

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.

Beschrä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 für mehrere Aufrufe oder die Freigabe des Zustands über mehrere Benutzersitzungen hinweg erforderlich sind. Wie bereits beschrieben, kann das Vorhandensein eines freigegebenen Zustands wichtige Ausnahmen verursachen, die sich auf die Skalierbarkeit und die Zuverlässigkeit der Anwendung auswirken.

In Anbetracht dieser Überlegungen wird die Verwendung statischer Variablen und statischer Datenmmber von Klassen, die in SQL Server verwendet werden, abgeraten. Für SAFE- und EXTERNAL_ACCESS assemblys untersucht SQL Server die Metadaten der Assembly zu CREATE ASSEMBLY Zeitpunkt 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 Attributen SharedState, Synchronizationund ExternalProcessMgmt Hostschutz 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 CLR-Integrationsprogrammiermodelleinschränkungen.