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 통합 보안을 참조하세요.
성능
데이터베이스 엔진 실행되는 관리되는 사용자 코드는 서버 외부에서 실행되는 동일한 코드와 비슷한 계산 성능을 가져야 합니다. 관리되는 사용자 코드에서 데이터베이스 액세스는 네이티브 Transact-SQL만큼 빠르지 않습니다. 자세한 내용은 CLR 통합의 성능을 참조 하세요.
CLR Services
CLR은 SQL Server와 CLR 통합의 디자인 목표를 달성하는 데 도움이 되는 다양한 서비스를 제공합니다.
형식 안전 확인
형식 안전 코드는 잘 정의된 방식으로만 메모리 구조에 액세스하는 코드입니다. 예를 들어 유효한 개체 참조가 있는 경우 형식 안전 코드는 실제 필드 멤버에 해당하는 고정 오프셋의 메모리에 액세스할 수 있습니다. 그러나 코드가 개체에 속하는 메모리 범위 내부 또는 외부의 임의 오프셋에서 메모리에 액세스하는 경우 형식이 안전하지 않습니다. CLR에서 어셈블리를 로드하는 경우 MSIL이 JIT(Just-In-Time) 컴파일을 사용하여 컴파일되기 전에 런타임은 코드를 검사하여 형식 안전성을 확인하는 확인 단계를 수행합니다. 이 확인 과정을 통과한 코드는 형식 안전 코드라고 할 수 있습니다.
애플리케이션 도메인
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가 공유 상태(예: 정적 클래스 필드)를 만들거나 관리하는 기능을 노출하는지 여부를 나타냅니다.
동기화는 API가 스레드 간에 동기화를 수행하는 기능을 노출하는지 여부를 나타냅니다.
ExternalProcessMgmt: API가 호스트 프로세스 제어 기능을 노출하는지 여부를 나타냅니다.
이러한 특성을 감안할 때 호스트는 호스트 환경에서 허용되지 않아야 하는 SharedState 특성과 같은 HPA 목록을 지정할 수 있습니다. 이 경우 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는 이러한 오류에 대한 응답으로 스레드 중단 예외를 발생합니다.
SQL Server에서 호스트되는 경우 이러한 스레드 중단은 다음과 같이 처리됩니다. CLR은 스레드 중단이 발생하는 애플리케이션 도메인에서 공유 상태를 검색합니다. CLR은 동기화 개체의 존재를 확인하여 이를 감지합니다. 애플리케이션 도메인에 공유 상태가 있는 경우 애플리케이션 도메인 자체가 언로드됩니다. 애플리케이션 도메인의 언로드가 해당 애플리케이션 도메인에서 현재 실행 중인 데이터베이스 트랜잭션을 중지합니다. 공유 상태의 존재는 예외를 트리거하는 예외가 아닌 사용자 세션에 대한 이러한 중요한 예외의 영향을 확대할 수 있으므로 SQL Server와 CLR은 공유 상태의 가능성을 줄이기 위한 조치를 취했습니다. 자세한 내용은 .NET Framework 설명서를 참조하세요.
보안: 권한 집합
SQL Server를 사용하면 사용자가 데이터베이스에 배포된 코드에 대한 안정성 및 보안 요구 사항을 지정할 수 있습니다. 어셈블리가 데이터베이스에 업로드되면 어셈블리 작성자는 해당 어셈블리에 대한 세 가지 권한 집합 중 하나인 SAFE, EXTERNAL_ACCESS 및 UNSAFE를 지정할 수 있습니다.
기능 | SAFE | EXTERNAL_ACCESS | UNSAFE |
---|---|---|---|
코드 액세스 보안 | 실행만 | 실행 및 외부 리소스 액세스 | 제한 없음 |
프로그래밍 모델 제한 | 예 | 예 | 제한 없음 |
안정성 요구 사항 | 예 | 예 | 아니요 |
네이티브 코드 호출 기능 | 아니요 | 아니요 | 예 |
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, Synchronization 및 ExternalProcessMgmt 호스트 보호 특성으로 주석이 추가된 .NET Framework API에 대한 호출을 허용하지 않습니다. 이렇게 하면 SAFE 및 EXTERNAL_ACCESS 어셈블리가 공유 상태를 사용하도록 설정하고 동기화를 수행하며 SQL Server 프로세스의 무결성에 영향을 주는 API를 호출할 수 없습니다. 자세한 내용은 CLR 통합 프로그래밍 모델 제한을 참조 하세요.