Delen via


CLR-integratiearchitectuur - gehoste CLR-omgeving

van toepassing op:SQL ServerAzure SQL Managed Instance

Met SQL Server-integratie met .NET Framework Common Language Runtime (CLR) kunnen databaseprogrammeurs talen gebruiken, zoals C#, Visual Basic .NET en Visual C++. Functies, opgeslagen procedures, triggers, gegevenstypen en aggregaties behoren tot de soorten bedrijfslogica die programmeurs met deze talen kunnen schrijven.

De CLR biedt garbage-verzamelde geheugen, preventieve threading, metagegevensservices (type weerspiegeling), controleerbaarheid van code en beveiliging van codetoegang. De CLR maakt gebruik van metagegevens om klassen te zoeken en te laden, instanties in het geheugen in te delen, methode-aanroepen op te lossen, systeemeigen code te genereren, beveiliging af te dwingen en contextgrenzen voor runtime in te stellen.

De CLR en SQL Server verschillen als runtime-omgevingen op de manier waarop ze geheugen, threads en synchronisatie verwerken. In dit artikel wordt beschreven hoe deze twee uitvoeringstijden worden geïntegreerd, zodat alle systeembronnen uniform worden beheerd. In dit artikel wordt ook beschreven hoe CLR-codetoegangsbeveiliging (CAS) en SQL Server-beveiliging zijn geïntegreerd om een betrouwbare en veilige uitvoeringsomgeving voor gebruikerscode te bieden.

Basisconcepten van CLR-architectuur

In .NET Framework schrijft een programmeur in een taal op hoog niveau die een klasse implementeert die de structuur (bijvoorbeeld de velden of eigenschappen van de klasse) en methoden definieert. Sommige van deze methoden kunnen statische functies zijn. De compilatie van het programma produceert een bestand met de naam een assembly die de gecompileerde code in de gecompileerde taal (CIL) bevat en een manifest dat alle verwijzingen naar afhankelijke assembly's bevat.

Notitie

Assembly's zijn een essentieel element in de architectuur van de CLR. Dit zijn de eenheden van pakketten, implementatie en versiebeheer van toepassingscode in .NET Framework. Met behulp van assembly's kunt u toepassingscode in de database implementeren en een uniforme manier bieden voor het beheren, maken van back-ups en het herstellen van volledige databasetoepassingen.

Het assemblymanifest bevat metagegevens over de assembly, waarin alle structuren, velden, eigenschappen, klassen, overnamerelaties, functies en methoden worden beschreven die in het programma zijn gedefinieerd. Het manifest stelt de assembly-identiteit vast, geeft de bestanden op waaruit de assembly-implementatie bestaat, specificeert de typen en resources waaruit de assembly bestaat, itemiseert de compilertijdafhankelijkheden van andere assembly's en geeft de set machtigingen op die nodig zijn om de assembly correct uit te voeren. Deze informatie wordt tijdens runtime gebruikt om verwijzingen op te lossen, versiebindingsbeleid af te dwingen en de integriteit van geladen assembly's te valideren.

.NET Framework ondersteunt aangepaste kenmerken voor het toevoegen van aantekeningen aan klassen, eigenschappen, functies en methoden met aanvullende informatie die de toepassing kan vastleggen in metagegevens. Alle .NET Framework-compilers gebruiken deze aantekeningen zonder interpretatie en slaan ze op als assemblymetagegevens. Deze aantekeningen kunnen op dezelfde manier worden onderzocht als andere metagegevens.

Beheerde code wordt uitgevoerd in de CLR in plaats van rechtstreeks door het besturingssysteem. Beheerde codetoepassingen verkrijgen CLR-services, zoals automatische garbagecollection, controle van runtimetypen en beveiligingsondersteuning. Deze services bieden uniform platform- en taalonafhankelijk gedrag van beheerde codetoepassingen.

Ontwerpdoelen van CLR-integratie

Wanneer gebruikerscode wordt uitgevoerd in de door CLR gehoste omgeving in SQL Server (ook wel CLR-integratie genoemd), zijn de volgende ontwerpdoelen van toepassing:

Betrouwbaarheid (veiligheid)

Gebruikerscode mag geen bewerkingen uitvoeren die de integriteit van het Database Engine-proces in gevaar brengen, zoals het poppen van een berichtvak dat een gebruikersantwoord aanvraagt of het proces afsluit. Gebruikerscode mag geen geheugenbuffers of interne gegevensstructuren van de database-engine overschrijven.

Schaalbaarheid

SQL Server en de CLR hebben verschillende interne modellen voor planning en geheugenbeheer. SQL Server ondersteunt een coöperatief, niet-preemptief threadingmodel waarin de threads periodiek uitvoering opleveren, of wanneer ze wachten op vergrendelingen of I/O. De CLR ondersteunt een preemptive threading-model. Als gebruikerscode die in SQL Server wordt uitgevoerd, de primitieven voor besturingssysteemthreading rechtstreeks kan aanroepen, kan deze niet goed worden geïntegreerd in de SQL Server-taakplanner en kan de schaalbaarheid van het systeem afnemen. De CLR maakt geen onderscheid tussen virtueel en fysiek geheugen, maar SQL Server beheert rechtstreeks fysiek geheugen en is vereist voor het gebruik van fysiek geheugen binnen een configureerbare limiet.

De verschillende modellen voor threading, planning en geheugenbeheer geven een integratievraag voor een relationeel databasebeheersysteem (RDBMS) dat wordt geschaald om duizenden gelijktijdige gebruikerssessies te ondersteunen. De architectuur moet ervoor zorgen dat de schaalbaarheid van het systeem niet wordt aangetast door gebruikerscode die application programming interfaces (API's) aanroept voor threading, geheugen en synchronisatieprimitief rechtstreeks.

Veiligheid

Gebruikerscode die wordt uitgevoerd in de database, moet voldoen aan sql Server-verificatie- en autorisatieregels bij het openen van databaseobjecten, zoals tabellen en kolommen. Daarnaast moeten databasebeheerders de toegang tot besturingssysteembronnen, zoals bestanden en netwerktoegang, kunnen beheren vanuit gebruikerscode die in de database wordt uitgevoerd. Deze procedure wordt belangrijk als beheerde programmeertalen (in tegenstelling tot niet-beheerde talen zoals Transact-SQL) bieden API's voor toegang tot dergelijke resources. Het systeem moet een veilige manier bieden voor gebruikerscode voor toegang tot machinebronnen buiten het database-engineproces. Zie CLR-integratiebeveiligingvoor meer informatie.

Voorstelling

Beheerde gebruikerscode die wordt uitgevoerd in de database-engine, moet rekenprestaties hebben die vergelijkbaar zijn met dezelfde code die buiten de server wordt uitgevoerd. Databasetoegang vanuit beheerde gebruikerscode is niet zo snel als systeemeigen Transact-SQL. Zie Performance of CLR Integration Architecturevoor meer informatie.

CLR-services

De CLR biedt verschillende services om de ontwerpdoelen van CLR-integratie met SQL Server te bereiken.

Typeveiligheidsverificatie

Typeveilige code is code die alleen op goed gedefinieerde manieren toegang heeft tot geheugenstructuren. Als u bijvoorbeeld een geldige objectverwijzing hebt, heeft typeveilige code toegang tot het geheugen op vaste offsets die overeenkomen met de werkelijke veldleden. Als de code echter toegang krijgt tot geheugen op willekeurige verschuivingen binnen of buiten het geheugenbereik dat tot het object behoort, is deze niet typeveilig. Wanneer assembly's in de CLR worden geladen voordat de CIL wordt gecompileerd met just-in-time compilatie, voert de runtime een verificatiefase uit waarmee code wordt onderzocht om de typeveiligheid te bepalen. Code die deze verificatie heeft doorgegeven, wordt verifieerbaar typeveilige code genoemd.

Toepassingsdomeinen

De CLR ondersteunt het begrip toepassingsdomeinen als uitvoeringszones binnen een hostproces waarin beheerde codeassembly's kunnen worden geladen en uitgevoerd. De grens van het toepassingsdomein biedt isolatie tussen assembly's. De assembly's worden geïsoleerd in termen van zichtbaarheid van statische variabelen en gegevensleden en de mogelijkheid om code dynamisch aan te roepen. Toepassingsdomeinen zijn ook het mechanisme voor het laden en lossen van code. Code kan alleen uit het geheugen worden verwijderd door het toepassingsdomein te lossen. Zie Application Domains en CLR Integration Securityvoor meer informatie.

Beveiliging van codetoegang (CAS)

Het CLR-beveiligingssysteem biedt een manier om te bepalen welke soorten beheerde code bewerkingen kunnen uitvoeren door machtigingen toe te wijzen aan code. Machtigingen voor codetoegang worden toegewezen op basis van code-identiteit (bijvoorbeeld de handtekening van de assembly of de oorsprong van de code).

De CLR biedt een computerbreed beleid dat kan worden ingesteld door de computerbeheerder. Dit beleid definieert de machtigingstoestemmingen voor alle beheerde code die op de computer wordt uitgevoerd. Daarnaast is er een beveiligingsbeleid op hostniveau dat kan worden gebruikt door hosts zoals SQL Server om aanvullende beperkingen voor beheerde code op te geven.

Als een beheerde API in .NET Framework bewerkingen beschikbaar maakt voor resources die worden beveiligd met een machtiging voor codetoegang, vraagt de API die machtiging voordat de resource wordt geopend. Deze vraag zorgt ervoor dat het CLR-beveiligingssysteem een uitgebreide controle activeert van elke code-eenheid (assembly) in de aanroepstack. Toegang tot de resource wordt alleen verleend als de hele aanroepketen machtigingen heeft.

De mogelijkheid om dynamisch beheerde code te genereren met behulp van de Reflection.Emit-API, wordt niet ondersteund in de door CLR gehoste omgeving in SQL Server. Dergelijke code zou niet de CAS-machtigingen hebben om uit te voeren en zou daarom mislukken tijdens runtime. Zie CLR Integration Code Access Securityvoor meer informatie.

Hostbeveiligingskenmerken (HPA's)

De CLR biedt een mechanisme voor het maken van aantekeningen bij beheerde API's die deel uitmaken van .NET Framework met bepaalde kenmerken die mogelijk van belang zijn voor een host van de CLR. Voorbeelden van dergelijke kenmerken zijn:

  • SharedState, waarmee wordt aangegeven of de API de mogelijkheid biedt om een gedeelde status te maken of te beheren (bijvoorbeeld statische klassevelden).

  • Synchronization, waarmee wordt aangegeven of de API de mogelijkheid biedt om synchronisatie tussen threads uit te voeren.

  • ExternalProcessMgmt, waarmee wordt aangegeven of de API een manier beschikbaar maakt om het hostproces te beheren.

Op basis van deze kenmerken kan de host een lijst met HPA's opgeven, zoals het kenmerk SharedState, die niet moeten worden toegestaan in de gehoste omgeving. In dit geval weigert de CLR pogingen van gebruikerscode om API's aan te roepen die zijn geannoteerd door de HPA's in de lijst verboden. Zie Hostbeveiligingskenmerken en CLR-integratieprogrammeringvoor meer informatie.

Hoe SQL Server en de CLR samenwerken

In deze sectie wordt beschreven hoe SQL Server de threading-, plannings-, synchronisatie- en geheugenbeheermodellen van SQL Server en de CLR integreert. In dit gedeelte wordt met name de integratie onderzocht in het licht van schaalbaarheid, betrouwbaarheid en beveiligingsdoelen. SQL Server fungeert in wezen als het besturingssysteem voor de CLR wanneer deze wordt gehost in SQL Server. De CLR roept routines op laag niveau aan die door SQL Server zijn geïmplementeerd voor threading, planning, synchronisatie en geheugenbeheer. Deze routines zijn dezelfde primitieven die de rest van de SQL Server-engine gebruikt. Deze benadering biedt verschillende schaalbaarheids-, betrouwbaarheids- en beveiligingsvoordelen.

Schaalbaarheid: Algemene threading, planning en synchronisatie

CLR roept SQL Server-API's aan voor het maken van threads, zowel voor het uitvoeren van gebruikerscode als voor eigen intern gebruik. Als u wilt synchroniseren tussen meerdere threads, roept de CLR SQL Server-synchronisatieobjecten aan. Met deze procedure kan de SQL Server-planner andere taken plannen wanneer een thread wacht op een synchronisatieobject. Wanneer de CLR bijvoorbeeld garbagecollection initieert, wachten alle threads tot de garbagecollection is voltooid. Omdat de CLR-threads en de synchronisatieobjecten waarop ze wachten, bekend zijn bij de SQL Server-scheduler, kan SQL Server threads plannen die andere databasetaken uitvoeren die geen betrekking hebben op de CLR. Hierdoor kan SQL Server ook impasses detecteren die betrekking hebben op vergrendelingen die zijn genomen door CLR-synchronisatieobjecten en traditionele technieken gebruiken voor het verwijderen van impasses.

Beheerde code wordt preventief uitgevoerd in SQL Server. De SQL Server-scheduler heeft de mogelijkheid om threads te detecteren en te stoppen die gedurende een aanzienlijke tijd niet hebben opgeleverd. De mogelijkheid om CLR-threads te koppelen aan SQL Server-threads impliceert dat de SQL Server-scheduler 'runaway'-threads in de CLR kan identificeren en hun prioriteit kan beheren. Dergelijke runaway threads worden onderbroken en worden weer in de wachtrij geplaatst. Threads die herhaaldelijk worden geïdentificeerd als runaway-threads, mogen gedurende een bepaalde periode niet worden uitgevoerd, zodat andere uit te voeren werkrollen kunnen worden uitgevoerd.

Er zijn enkele situaties waarin langlopende beheerde code automatisch oplevert, en sommige situaties waarin dit niet gebeurt. In de volgende situaties levert langlopende beheerde code automatisch op:

  • Als de code het SQL-besturingssysteem aanroept (bijvoorbeeld om een query uit te voeren op gegevens)
  • Als er voldoende geheugen is toegewezen om garbagecollection te activeren
  • Als de code in de preemptieve modus komt door os-functies aan te roepen

Code die geen van deze acties uitvoert, zoals strakke lussen die alleen berekeningen bevatten, levert niet automatisch de scheduler op, wat kan leiden tot lange wachttijden voor andere workloads in het systeem. In deze situaties is het aan de ontwikkelaar om expliciet rendement te behalen door de System.Thread.Sleep()-functie van .NET Framework aan te roepen, of door expliciet de voorlopige modus in te voeren met System.Thread.BeginThreadAffinity(), in alle secties met code die naar verwachting langdurig zijn. In de volgende codevoorbeelden ziet u hoe u handmatig kunt opleveren met behulp van elk van deze methoden.

Voorbeelden

Handmatig rendement naar SOS-planner

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);
  }
}

ThreadAffinity gebruiken om preventief uit te voeren

In dit voorbeeld wordt de CLR-code uitgevoerd in de preventieve modus binnen BeginThreadAffinity en EndThreadAffinity.

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();

Schaalbaarheid: Algemeen geheugenbeheer

De CLR roept PRIMITIEVEN van SQL Server aan voor het toewijzen en toewijzen van het geheugen. Omdat het geheugen dat door de CLR wordt gebruikt, wordt verantwoordelijk voor het totale geheugengebruik van het systeem, kan SQL Server binnen de geconfigureerde geheugenlimieten blijven en ervoor zorgen dat de CLR en SQL Server niet met elkaar concurreren voor geheugen. SQL Server kan ook CLR-geheugenaanvragen weigeren wanneer het systeemgeheugen is beperkt en clr vragen om het geheugengebruik te verminderen wanneer andere taken geheugen nodig hebben.

Betrouwbaarheid: toepassingsdomeinen en onherstelbare uitzonderingen

Wanneer beheerde code in de .NET Framework-API's kritieke uitzonderingen tegenkomt, zoals onvoldoende geheugen of stack-overloop, is het niet altijd mogelijk om dergelijke fouten te herstellen en consistente en juiste semantiek voor hun implementatie te garanderen. Deze API's veroorzaken een thread die een uitzondering afbreekt als reactie op deze fouten.

Wanneer deze thread wordt gehost in SQL Server, worden dergelijke threads als volgt verwerkt: de CLR detecteert een gedeelde status in het toepassingsdomein waarin de thread wordt afgebroken. De CLR detecteert dit door te controleren op de aanwezigheid van synchronisatieobjecten. Als het toepassingsdomein een gedeelde status heeft, wordt het toepassingsdomein zelf verwijderd. Het verwijderen van het toepassingsdomein stopt databasetransacties die momenteel worden uitgevoerd in dat toepassingsdomein. Omdat de aanwezigheid van een gedeelde status het effect van dergelijke kritieke uitzonderingen op andere gebruikerssessies kan vergroten dan degene die de uitzondering activeert, hebben SQL Server en de CLR stappen ondernomen om de kans op gedeelde status te verminderen. Zie .NET Frameworkvoor meer informatie.

Beveiliging: machtigingensets

Met SQL Server kunnen gebruikers de betrouwbaarheids- en beveiligingsvereisten opgeven voor code die in de database is geïmplementeerd. Wanneer assembly's worden geüpload naar de database, kan de auteur van de assembly een van de drie machtigingensets voor die assembly opgeven: SAFE, EXTERNAL_ACCESSen UNSAFE.

Functionaliteit SAFE EXTERNAL_ACCESS UNSAFE
Code Access Security Alleen uitvoeren Uitvoeren en toegang tot externe resources Onbeperkt
Programming model restrictions Ja Ja Geen beperkingen
Verifiability requirement Ja Ja Nee
Ability to call native code Nee Nee Ja

SAFE is de meest betrouwbare en veilige modus met bijbehorende beperkingen in termen van het toegestane programmeermodel. SAFE assembly's voldoende machtigingen hebben om uit te voeren, berekeningen uit te voeren en toegang te hebben tot de lokale database. SAFE assembly's moeten verifieerbaar veilig zijn en zijn niet toegestaan om onbeheerde code aan te roepen.

UNSAFE is bedoeld voor zeer vertrouwde code die alleen kan worden gemaakt door databasebeheerders. Deze vertrouwde code heeft geen beveiligingsbeperkingen voor codetoegang en kan onbeheerde (systeemeigen) code aanroepen.

EXTERNAL_ACCESS biedt een tussenliggende beveiligingsoptie, waardoor code toegang heeft tot resources buiten de database, maar nog steeds de betrouwbaarheidsgaranties van SAFEhebben.

SQL Server maakt gebruik van de CAS-beleidslaag op hostniveau om een hostbeleid in te stellen dat een van de drie sets machtigingen verleent op basis van de machtigingenset die is opgeslagen in SQL Server-catalogi. Beheerde code die in de database wordt uitgevoerd, krijgt altijd een van deze machtigingensets voor codetoegang.

Beperkingen voor programmeermodellen

Het programmeermodel voor beheerde code in SQL Server omvat het schrijven van functies, procedures en typen waarvoor doorgaans geen gebruik van status is vereist voor meerdere aanroepen of het delen van de status voor meerdere gebruikerssessies. Zoals eerder is beschreven, kan de aanwezigheid van de gedeelde status kritieke uitzonderingen veroorzaken die van invloed zijn op de schaalbaarheid en de betrouwbaarheid van de toepassing.

Gezien deze overwegingen raden we het gebruik van statische variabelen en statische gegevensleden aan van klassen die worden gebruikt in SQL Server. Voor SAFE en EXTERNAL_ACCESS assembly's onderzoekt SQL Server de metagegevens van de assembly op CREATE ASSEMBLY moment en mislukt het maken van dergelijke assembly's als het gebruik van statische gegevensleden en variabelen wordt gevonden.

SQL Server staat ook geen aanroepen toe naar .NET Framework-API's die zijn geannoteerd met de SharedState, Synchronizationen ExternalProcessMgmt hostbeveiligingskenmerken. Hiermee voorkomt u dat SAFE en EXTERNAL_ACCESS assembly's API's aanroepen die de status delen inschakelen, synchronisatie uitvoeren en de integriteit van het SQL Server-proces beïnvloeden. Zie beperkingen voor clr-integratiemodellenvoor meer informatie.