CLR 통합 아키텍처 - CLR 호스팅 환경

적용 대상: SQL Server Azure SQL Managed Instance

.NET Framework CLR(공용 언어 런타임)과 SQL Server 통합하면 데이터베이스 프로그래머가 Visual C#, Visual Basic .NET 및 Visual C++와 같은 언어를 사용할 수 있습니다. 프로그래머가 이러한 언어를 사용하여 작성할 수 있는 비즈니스 논리의 종류에는 함수, 저장 프로시저, 트리거, 데이터 형식, 집계 등이 포함됩니다.

CLR은 가비지 수집 메모리, 선점 스레딩, 메타데이터 서비스(형식 리플렉션), 코드 확인 가능성 및 코드 액세스 보안을 제공합니다. CLR에서는 메타데이터를 사용하여 클래스를 찾아 로드하고, 메모리에 인스턴스를 배치하고, 메서드 호출을 확인하고, 네이티브 코드를 생성하고, 보안을 강화하며, 런타임 컨텍스트 경계를 설정합니다.

CLR 및 SQL Server 메모리, 스레드 및 동기화를 처리하는 방식에서 런타임 환경과 다릅니다. 이 문서에서는 모든 시스템 리소스가 균일하게 관리되도록 이러한 두 런타임을 통합하는 방법을 설명합니다. 이 문서에서는 사용자 코드에 대한 안정적이고 안전한 실행 환경을 제공하기 위해 CLR CAS(코드 액세스 보안) 및 SQL Server 보안을 통합하는 방법에 대해서도 설명합니다.

CLR 아키텍처의 기본 개념

.NET Framework에서 프로그래머는 클래스의 구조(예: 클래스의 필드나 속성)와 메서드를 정의하여 클래스를 구현하는 고급 언어로 코드를 작성합니다. 이러한 메서드 일부는 정적 함수일 수 있습니다. 프로그램의 컴파일은 MSIL(Microsoft Intermediate Language)의 컴파일된 코드를 포함하는 어셈블리라는 파일과 종속 어셈블리에 대한 모든 참조를 포함하는 매니페스트를 생성합니다.

참고

어셈블리는 CLR 아키텍처의 필수 요소로, .NET Framework에서 애플리케이션 코드를 패키징, 배포 및 버전 관리하는 단위입니다. 어셈블리를 사용하면 데이터베이스 안에 애플리케이션 코드를 배포하고 완전한 데이터베이스 애플리케이션을 일관된 방법으로 관리, 백업 및 복원할 수 있습니다.

어셈블리 매니페스트에는 프로그램에 정의되어 있는 모든 구조, 필드, 속성, 클래스, 상속 관계, 함수 및 메서드를 설명하는 어셈블리 메타데이터가 들어 있습니다. 어셈블리 매니페스트는 어셈블리의 ID를 설정하고, 어셈블리 구현을 구성하는 파일을 지정하고, 어셈블리를 구성하는 형식 및 리소스를 지정하며, 컴파일 타임의 다른 어셈블리에 대한 종속성을 항목별로 요약하고, 어셈블리가 제대로 실행되는 데 필요한 권한 집합을 지정합니다. 이 정보는 런타임에 참조를 확인하고, 버전 바인딩 정책을 적용하고, 로드된 어셈블리의 무결성을 확인하는 데 사용됩니다.

.NET Framework에서는 애플리케이션에서 메타데이터에 캡처할 수 있는 추가적인 정보를 클래스, 속성, 함수 및 메서드에 주석으로 추가할 수 있는 사용자 지정 특성을 지원합니다. 모든 .NET Framework 컴파일러는 이러한 주석을 해석 없이 사용하고 어셈블리 메타데이터로 저장합니다. 이러한 주석은 다른 메타데이터와 같은 방식으로 검사할 수 있습니다.

관리 코드는 운영 체제에서 직접 실행되는 대신 CLR에서 실행되는 MSIL입니다. 관리 코드 애플리케이션은 자동 가비지 수집, 런타임 형식 확인, 보안 지원 등과 같은 CLR 서비스를 얻습니다. 이러한 서비스는 관리 코드 애플리케이션의 일관된 플랫폼 및 언어 독립적인 동작을 제공하는 데 도움이 됩니다.

CLR 통합의 디자인 목표

사용자 코드가 SQL Server CLR 호스팅 환경 내에서 실행되는 경우(CLR 통합이라고 함) 다음 디자인 목표가 적용됩니다.

안정성(보안)

사용자 코드는 사용자 응답을 요청하는 메시지 상자를 표시하거나 프로세스를 종료하는 것과 같이 데이터베이스 엔진 프로세스의 무결성을 손상시키는 작업을 수행할 수 없습니다. 또한 사용자 코드는 데이터베이스 엔진 메모리 버퍼 또는 내부 데이터 구조를 덮어쓸 수 없습니다.

확장성

SQL Server 및 CLR에는 예약 및 메모리 관리를 위한 서로 다른 내부 모델이 있습니다. SQL Server 스레드가 주기적으로 또는 잠금 또는 I/O를 기다리는 경우 스레드가 자발적으로 실행을 생성하는 협조적이고 선점적이지 않은 스레딩 모델을 지원합니다. 반면 CLR은 선점형 스레딩 모델을 지원합니다. SQL Server 내에서 실행되는 사용자 코드가 운영 체제 스레딩 기본 형식을 직접 호출할 수 있는 경우 SQL Server 작업 스케줄러에 잘 통합되지 않고 시스템의 확장성을 저하시킬 수 있습니다. CLR은 가상 메모리와 실제 메모리를 구분하지 않지만 SQL Server 직접 실제 메모리를 관리하고 구성 가능한 한도 내에서 실제 메모리를 사용해야 합니다.

수천 개의 동시 사용자 세션을 지원하도록 확장 가능한 RDBMS(관계형 데이터베이스 관리 시스템)의 경우에는 스레딩, 일정 예약 및 메모리 관리 모델이 다르기 때문에 통합하기 어렵습니다. 아키텍처에서는 스레딩, 메모리 및 동기화 기본 형식을 위한 API(응용 프로그래밍 인터페이스)를 직접 호출하는 사용자 코드로 인해 시스템의 확장성에 문제가 발생하지 않아야 합니다.

보안

데이터베이스에서 실행되는 사용자 코드는 테이블 및 열과 같은 데이터베이스 개체에 액세스할 때 SQL Server 인증 및 권한 부여 규칙을 따라야 합니다. 또한 데이터베이스 관리자는 데이터베이스에서 실행되는 사용자 코드를 통해 파일 같은 운영 체제 리소스에 대한 액세스 및 네트워크 액세스를 제어할 수 있어야 합니다. 이 방법은 관리되는 프로그래밍 언어(Transact-SQL과 같은 관리되지 않는 언어와 달리)가 이러한 리소스에 액세스하기 위해 API를 제공함에 따라 중요해집니다. 시스템은 사용자 코드가 데이터베이스 엔진 프로세스 외부의 컴퓨터 리소스에 액세스할 수 있는 안전한 방법을 제공해야 합니다. 자세한 내용은 CLR Integration Security을 참조하세요.

성능

데이터베이스 엔진에서 실행되는 관리되는 사용자 코드는 서버 외부에서 실행되는 동일한 코드와 비슷한 계산 성능을 가져야 합니다. 관리되는 사용자 코드에서 데이터베이스 액세스는 네이티브 Transact-SQL만큼 빠르지 않습니다. 자세한 내용은 CLR 통합의 성능을 참조하세요.

CLR Services

CLR은 SQL SERVER CLR 통합의 디자인 목표를 달성하는 데 도움이 되는 다양한 서비스를 제공합니다.

형식 안전성 확인

형식 안전 코드는 명확하게 정의된 방법으로만 메모리 구조에 액세스하는 코드입니다. 예를 들어 올바른 개체 참조에 대해 형식 안전 코드는 실제 필드 멤버에 대응하는 고정 오프셋에서 메모리에 액세스할 수 있습니다. 그러나 개체에 속하는 메모리 범위 안이나 밖의 임의의 오프셋에서 메모리에 액세스하는 코드는 형식 안전 코드가 아닙니다. 어셈블리가 CLR에 로드되고 JIT(Just-In-Time) 컴파일을 사용하여 MSIL을 컴파일되기 전에 런타임에서는 코드의 형식 안전성을 검사하는 확인 단계를 수행합니다. 이 확인 과정을 통과한 코드는 형식 안전 코드라고 할 수 있습니다.

애플리케이션 도메인

CLR은 호스트 프로세스에서 관리 코드 어셈블리를 로드하고 실행할 수 있는 실행 영역이라는 개념으로 애플리케이션 도메인을 지원합니다. 애플리케이션 도메인 경계는 어셈블리 사이를 구분합니다. 어셈블리는 정적 변수와 데이터 멤버의 표시 유형 및 코드를 동적으로 호출할 수 있는지 여부를 기준으로 격리됩니다. 애플리케이션 도메인은 코드를 로드하고 언로드하는 메커니즘이기도 합니다. 메모리에서 코드를 언로드하는 유일한 방법은 애플리케이션 도메인을 언로드하는 것입니다. 자세한 내용은 애플리케이션 도메인 및 CLR 통합 보안을 참조하세요.

CAS(코드 액세스 보안)

CLR 보안 시스템을 사용하면 코드에 사용 권한을 할당하여 관리 코드로 수행할 수 있는 작업의 유형을 제어할 수 있습니다. 코드 액세스 권한은 코드 ID(예: 어셈블리의 서명 또는 코드 원본)를 기준으로 할당됩니다.

CLR에는 컴퓨터 관리자가 설정할 수 있는 컴퓨터 차원의 정책이 있습니다. 이 정책은 컴퓨터에서 실행되는 모든 관리 코드에 대한 권한 부여를 정의합니다. 또한 SQL Server 같은 호스트에서 관리 코드에 대한 추가 제한을 지정하는 데 사용할 수 있는 호스트 수준 보안 정책이 있습니다.

.NET Framework의 관리되는 API가 코드 액세스 권한으로 보호되는 리소스에 대한 작업을 노출할 경우, API는 리소스에 액세스하기 전에 해당 사용 권한을 요청합니다. 이 요청이 발생하면 CLR 보안 시스템에서는 호출 스택에 포함된 모든 코드 단위(어셈블리)에 대한 포괄적인 검사를 트리거합니다. 리소스에 대한 액세스 권한은 전체 호출 체인에 권한이 있는 경우에만 부여됩니다.

Reflection.Emit API를 사용하여 관리 코드를 동적으로 생성하는 기능은 SQL Server CLR 호스팅 환경 내에서 지원되지 않습니다. 이러한 코드는 적절한 CAS 권한을 가지고 있지 않기 때문에 런타임에 실패합니다. 자세한 내용은 CLR 통합 코드 액세스 보안을 참조하세요.

HPA(호스트 보호 특성)

CLR은 .NET Framework의 일부인 관리되는 API에 대해 CLR 호스트에서 유용할 수 있는 특성으로 주석을 지정하기 위한 메커니즘을 제공합니다. 이러한 특성의 예를 들면 다음과 같습니다.

  • SharedState: API가 공유 상태(예: 정적 클래스 필드)를 만들거나 관리하는 기능을 노출하는지 여부를 나타냅니다.

  • Synchronization: API가 스레드 간 동기화 기능을 노출하는지 여부를 나타냅니다.

  • ExternalProcessMgmt: API가 호스트 프로세스 제어 기능을 노출하는지 여부를 나타냅니다.

이러한 특성을 사용하면 호스트에서는 호스팅 환경에서 허용하지 않는 HPA 목록(예: SharedState 특성)을 지정할 수 있습니다. 이렇게 하면 CLR에서는 금지 목록에 포함된 HPA로 주석이 지정된 API를 호출하는 사용자 코드를 거부합니다. 자세한 내용은 호스트 보호 특성 및 CLR 통합 프로그래밍을 참조하세요.

SQL Server와 CLR이 함께 작동하는 방법

이 섹션에서는 SQL Server SQL Server 및 CLR의 스레딩, 예약, 동기화 및 메모리 관리 모델을 통합하는 방법에 대해 설명합니다. 특히 이 섹션에서는 확장성, 안정성 및 보안 목표 측면에서의 통합을 검사합니다. SQL Server 기본적으로 SQL SERVER 내에서 호스트되는 CLR의 운영 체제 역할을 합니다. CLR은 스레딩, 예약, 동기화 및 메모리 관리를 위해 SQL Server 구현한 하위 수준 루틴을 호출합니다. 이러한 루틴은 나머지 SQL Server 엔진에서 사용하는 것과 동일한 기본 형식입니다. 이 방식을 사용하면 확장성, 안정성 및 보안상 여러 가지 이점을 얻을 수 있습니다.

확장성: 일반적인 스레딩, 일정 및 동기화

CLR은 사용자 코드를 실행하고 자체 내부 사용을 위해 스레드를 만들기 위해 SQL Server API를 호출합니다. 여러 스레드 간에 동기화하기 위해 CLR은 SQL Server 동기화 개체를 호출합니다. 이 방법을 사용하면 스레드가 동기화 개체에서 대기하는 경우 SQL Server 스케줄러가 다른 작업을 예약할 수 있습니다. 예를 들어 CLR에서 가비지 수집을 시작하면 가비지 수집이 완료될 때까지 모든 스레드가 대기합니다. CLR 스레드 및 대기 중인 동기화 개체는 SQL Server 스케줄러에 알려지므로 SQL Server CLR과 관련이 없는 다른 데이터베이스 작업을 실행하는 스레드를 예약할 수 있습니다. 또한 이를 통해 SQL Server CLR 동기화 개체에서 가져온 잠금을 포함하는 교착 상태를 감지하고 교착 상태 제거를 위한 기존 기술을 사용할 수 있습니다.

관리 코드는 SQL Server 선제적으로 실행됩니다. SQL Server 스케줄러는 상당한 시간 동안 생성되지 않은 스레드를 검색하고 중지할 수 있습니다. CLR 스레드를 SQL Server 스레드에 연결하는 기능은 SQL Server 스케줄러가 CLR에서 "런어웨이" 스레드를 식별하고 우선 순위를 관리할 수 있음을 의미합니다. 이와 같은 런어웨이 스레드는 일시 중지되어 큐에 다시 배치됩니다. 런어웨이 스레드로 반복적으로 식별되는 스레드는 작업을 수행하는 다른 스레드가 실행될 수 있도록 일정 기간 동안 일시 중지됩니다.

장기 실행 관리 코드가 자동으로 생성되는 몇 가지 상황과 그렇지 않은 경우도 있습니다. 다음 상황에서 장기 실행 관리 코드는 자동으로 생성됩니다.

  • 코드가 SQL OS를 호출하는 경우(예: 데이터를 쿼리하기 위해)
  • 가비지 수집을 트리거하기에 충분한 메모리가 할당된 경우
  • 코드가 OS 함수를 호출하여 선점 모드로 전환되는 경우

위의 작업을 수행하지 않는 코드(예: 계산만 포함된 타이트 루프)는 스케줄러를 자동으로 생성하지 않으므로 시스템의 다른 워크로드에 대한 대기 시간이 길어질 수 있습니다. 이러한 상황에서는 개발자가 .NET Framework System.Thread.Sleep() 함수를 호출하거나 장기 실행될 것으로 예상되는 코드 섹션에서 System.Thread.BeginThreadAffinity()를 사용하여 선점 모드를 명시적으로 입력하여 명시적으로 양보해야 합니다. 다음 코드 예제에서는 이러한 각 메서드를 사용하여 수동으로 생성하는 방법을 보여 줍니다.

// 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();
확장성: 일반적인 메모리 관리

CLR은 메모리 할당 및 할당 해제를 위해 SQL Server 기본 형식을 호출합니다. CLR에서 사용하는 메모리는 시스템의 총 메모리 사용량에서 고려되기 때문에 SQL Server 구성된 메모리 제한 내에서 유지되고 CLR 및 SQL Server 메모리를 위해 서로 경쟁하지 않도록 할 수 있습니다. SQL Server 시스템 메모리가 제한될 때 CLR 메모리 요청을 거부하고 다른 작업에 메모리가 필요할 때 CLR에 메모리 사용을 줄이도록 요청할 수도 있습니다.

안정성: 애플리케이션 도메인 및 복구할 수 없는 예외

.NET Framework API의 관리 코드에서 메모리 부족 또는 스택 오버플로와 같은 중대한 예외가 발견되었을 때 이러한 오류를 복구하여 API 구현의 의미를 일관되고 올바르게 유지하지 못하는 경우도 있습니다. 이러한 API는 이와 같은 오류에 대한 응답으로 스레드 중단 예외를 발생시킵니다.

SQL Server 호스트되는 경우 이러한 스레드 중단은 다음과 같이 처리됩니다. CLR은 스레드 중단이 발생하는 애플리케이션 도메인에서 공유 상태를 검색합니다. CLR은 동기화 개체의 존재를 확인하여 이를 검색합니다. 애플리케이션 도메인에 공유 상태가 있으면 애플리케이션 도메인 자체가 언로드됩니다. 애플리케이션 도메인이 언로드되면 해당 애플리케이션 도메인에서 현재 실행 중인 데이터베이스 트랜잭션이 중지됩니다. 공유 상태의 존재는 예외를 트리거하는 세션 이외의 사용자 세션에 대한 이러한 중요한 예외의 영향을 확대할 수 있으므로 SQL Server CLR은 공유 상태의 가능성을 줄이기 위한 조치를 취했습니다. 자세한 내용은 .NET Framework 설명서를 참조하십시오.

보안: 권한 집합

SQL Server 사용자가 데이터베이스에 배포된 코드에 대한 안정성 및 보안 요구 사항을 지정할 수 있습니다. 어셈블리가 데이터베이스에 업로드되면 어셈블리 작성자는 해당 어셈블리에 대한 SAFE, EXTERNAL_ACCESS 및 UNSAFE의 세 가지 권한 집합 중 하나를 지정할 수 있습니다.

기능 SAFE EXTERNAL_ACCESS UNSAFE
코드 액세스 보안 실행 전용 실행 및 외부 리소스 액세스 제한 없음
프로그래밍 모델 제한 사항 제한 없음
안정성 요구 사항 Yes
네이티브 코드를 호출하는 기능

SAFE는 가장 신뢰할 수 있고 안전한 모드로, 허용되는 프로그래밍 모델에 대한 제한이 있습니다. SAFE 어셈블리는 실행하고, 계산을 수행하고, 로컬 데이터베이스에 액세스할 수 있는 권한이 부여됩니다. SAFE 어셈블리는 확인할 수 있는 형식 안전 어셈블리여야 하며 비관리 코드를 호출할 수 없습니다.

UNSAFE는 데이터베이스 관리자만 만들 수 있는 신뢰 수준이 높은 코드용입니다. 이 신뢰되는 코드는 코드 액세스 보안 제한이 없으며 비관리(네이티브) 코드를 호출할 수 있습니다.

EXTERNAL_ACCESS는 중급 보안 옵션을 제공하며 코드가 데이터베이스 외부의 리소스에 액세스하도록 허용하지만 여전히 SAFE 수준의 안정성과 보안을 갖습니다.

SQL Server 호스트 수준 CAS 정책 계층을 사용하여 SQL Server 카탈로그에 저장된 권한 집합에 따라 세 가지 권한 집합 중 하나를 부여하는 호스트 정책을 설정합니다. 데이터베이스 내부에서 실행되는 관리 코드에는 항상 이러한 코드 액세스 권한 집합 중 하나가 부여됩니다.

프로그래밍 모델 제한 사항

SQL Server 관리 코드에 대한 프로그래밍 모델에는 일반적으로 여러 호출에서 유지되는 상태를 사용하거나 여러 사용자 세션에서 상태를 공유할 필요가 없는 함수, 프로시저 및 형식 작성이 포함됩니다. 또한 앞서 설명한 것과 같이 공유된 상태가 있으면 해당 애플리케이션의 확장성과 안정성에 영향을 주는 중대한 예외가 발생할 수 있습니다.

이러한 고려 사항을 고려할 때 SQL Server 사용되는 클래스의 정적 변수 및 정적 데이터 멤버를 사용하지 않도록 합니다. SAFE 및 EXTERNAL_ACCESS 어셈블리의 경우 SQL Server CREATE ASSEMBLY 시간에 어셈블리의 메타데이터를 검사하고 정적 데이터 멤버 및 변수의 사용을 발견하면 이러한 어셈블리를 만들지 못합니다.

또한 SQL Server SharedState, SynchronizationExternalProcessMgmt 호스트 보호 특성으로 주석이 추가된 .NET Framework API에 대한 호출을 허용하지 않습니다. 이렇게 하면 SAFE 및 EXTERNAL_ACCESS 어셈블리가 공유 상태를 사용하도록 설정하고 동기화를 수행하며 SQL Server 프로세스의 무결성에 영향을 주는 API를 호출할 수 없습니다. 자세한 내용은 CLR 통합 프로그래밍 모델 제한을 참조하세요.

참고 항목

CLR 통합 보안
통합된 CLR의 성능