CLR 통합 아키텍처 - 성능

적용 대상: SQL Server Azure SQL Managed Instance

이 항목에서는 Microsoft .NET Framework CLR(공용 언어 런타임)과 Microsoft SQL Server 통합의 성능을 향상시키는 몇 가지 디자인 선택에 대해 설명합니다.

컴파일 프로세스

SQL 식을 컴파일하는 동안 관리되는 루틴에 대한 참조가 발견되면 MSIL(Microsoft Intermediate Language) 스텁이 생성됩니다. 이 스텁에는 루틴 매개 변수를 SQL Server CLR로 마샬링하고, 함수를 호출하고, 결과를 반환하는 코드가 포함되어 있습니다. 이러한 "글루" 코드는 매개 변수의 형식과 매개 변수 방향(입력, 출력 또는 참조)에 기반을 둡니다.

붙이기 코드는 형식별 최적화를 사용하도록 설정하고 nullability, 제약 패싯, 값별 및 표준 예외 처리와 같은 SQL Server 의미 체계를 효율적으로 적용할 수 있도록 합니다. 인수의 정확한 형식에 맞는 코드를 생성하면 호출 경계를 넘어 형식 강제 변환이 수행되거나 boxing이라는 래퍼 개체 생성 비용이 발생하는 것을 방지할 수 있습니다.

생성된 스텁은 네이티브 코드로 컴파일되고 CLR의 JIT(Just-In-Time) 컴파일 서비스를 사용하여 SQL Server 실행되는 특정 하드웨어 아키텍처에 최적화됩니다. JIT 서비스는 메서드 수준에서 호출되며 SQL Server 호스팅 환경에서 SQL Server 및 CLR 실행에 걸쳐 있는 단일 컴파일 단위를 만들 수 있습니다. 스텁이 컴파일되고 나면 결과 함수 포인터가 함수의 런타임 구현이 됩니다. 이러한 코드 생성 방식을 사용하면 런타임에 리플렉션이나 메타데이터 액세스와 관련된 추가 호출 비용이 발생하지 않습니다.

SQL Server 및 CLR 간의 빠른 전환

컴파일 프로세스를 수행하면 런타임에 네이티브 코드에서 호출할 수 있는 함수 포인터가 생성됩니다. 스칼라 반환 사용자 정의 함수의 경우 이 함수 호출은 행 단위로 수행됩니다. SQL Server CLR 간의 전환 비용을 최소화하기 위해 관리되는 호출을 포함하는 문에는 대상 애플리케이션 도메인을 식별하는 시작 단계가 있습니다. 이러한 확인 단계를 통해 각 행에서 전환 비용을 줄일 수 있습니다.

성능 고려 사항

다음은 SQL Server CLR 통합과 관련된 성능 고려 사항을 요약한 것입니다. 자세한 내용은 MSDN 웹 사이트의 "SQL Server 2005에서 CLR 통합 사용"에서 찾을 수 있습니다. 관리 코드 성능에 대한 일반적인 정보는 MSDN 웹 사이트의 ".NET 애플리케이션 성능 및 확장성 개선"에서 찾을 수 있습니다.

사용자 정의 함수

CLR 함수는 Transact-SQL 사용자 정의 함수보다 더 빠른 호출 경로를 활용합니다. 또한 관리 코드는 절차 코드, 계산 및 문자열 조작 측면에서 Transact-SQL에 비해 결정적인 성능 이점이 있습니다. 계산을 많이 수행하고 데이터 액세스는 수행하지 않는 CLR 함수는 관리 코드로 작성하는 것이 좋습니다. 그러나 Transact-SQL 함수는 CLR 통합보다 데이터 액세스를 더 효율적으로 수행합니다.

사용자 정의 집계

관리 코드는 커서 기반 집계보다 성능이 훨씬 뛰어나지만 관리 코드는 일반적으로 기본 제공 SQL Server 집계 함수보다 약간 느리게 수행됩니다. 네이티브 기본 제공 집계 함수가 있는 경우에는 이를 사용하는 것이 좋습니다. 필요한 집계가 기본적으로 지원되지 않는 경우에는 성능을 위해 커서 기반 구현보다는 CLR 사용자 정의 집계를 사용하는 것이 좋습니다.

스트리밍 테이블 반환 함수

애플리케이션에서는 함수 호출의 결과로 테이블을 반환해야 하는 경우가 많습니다. 가져오기 작업의 일부로 파일에서 표 형식 데이터를 읽거나 쉼표로 구분된 값을 관계형 표현으로 변환하는 경우를 예로 들 수 있습니다. 일반적으로 이러한 작업은 호출자가 소비하기 전에 결과 테이블을 구체화하고 채우는 방법으로 수행할 수 있습니다. CLR을 SQL Server 통합하면 STVF(스트리밍 테이블 반환 함수)라는 새로운 확장성 메커니즘이 도입되었습니다. 관리되는 STVF는 비슷한 기능의 확장 저장 프로시저 구현보다 성능이 우수합니다.

STVF는 IEnumerable 인터페이스를 반환하는 관리되는 함수입니다. IEnumerable 에는 STVF에서 반환한 결과 집합을 탐색하는 메서드가 있습니다. STVF가 호출되면 반환된 IEnumerable 이 쿼리 계획에 직접 연결됩니다. 쿼리 계획은 행을 가져와야 할 때 IEnumerable 메서드를 호출합니다. 이 반복 모델을 사용하면 전체 테이블이 채워질 때까지 기다리지 않고 첫 번째 행이 생성되면 즉시 결과를 소비할 수 있습니다. 또한 함수 호출에 소비되는 메모리 양을 크게 줄일 수 있습니다.

배열 및 커서 비교

Transact-SQL 커서가 배열로 더 쉽게 표현되는 데이터를 트래버스해야 하는 경우 관리 코드는 상당한 성능 향상과 함께 사용할 수 있습니다.

문자열 데이터

varchar와 같은 SQL Server 문자 데이터는 관리되는 함수의 SqlString 또는 SqlChars 형식일 수 있습니다. SqlString 변수는 메모리에 전체 값의 인스턴스를 만듭니다. SqlChars 변수는 메모리에 전체 값의 인스턴스를 만들지 않고도 성능 및 확장성을 개선하는 데 사용할 수 있는 스트리밍 인터페이스를 제공합니다. LOB(큰 개체) 데이터의 경우 이러한 기능은 특히 중요합니다. 또한 SqlXml.CreateReader()에서 반환하는 스트리밍 인터페이스를 통해 서버 XML 데이터에 액세스할 수 있습니다.

CLR 및 확장 저장 프로시저 비교

관리되는 프로시저가 결과 집합을 클라이언트로 다시 보낼 수 있도록 지원하는 Microsoft.SqlServer.Server API(애플리케이션 프로그래밍 인터페이스)는 확장 저장 프로시저에서 사용되는 ODS(개방형 Data Services) API보다 성능이 우수합니다. 또한 System.Data.SqlServer API는 SQL Server 2005(9.x)에 도입된 xml, varchar(max), nvarchar(max)varbinary(max))와 같은 데이터 형식을 지원하지만 ODS API는 새 데이터 형식을 지원하도록 확장되지 않았습니다.

관리 코드를 사용하여 SQL Server 메모리, 스레드 및 동기화와 같은 리소스의 사용을 관리합니다. 이러한 리소스를 노출하는 관리되는 API가 SQL Server 리소스 관리자 위에 구현되기 때문입니다. 반대로 SQL Server 확장 저장 프로시저의 리소스 사용을 보거나 제어할 수 없습니다. 예를 들어 확장 저장 프로시저에서 CPU 또는 메모리 리소스를 너무 많이 사용하는 경우 SQL Server 이를 검색하거나 제어할 수 있는 방법이 없습니다. 그러나 관리 코드를 사용하면 SQL Server 지정된 스레드가 오랜 시간 동안 생성되지 않은 것을 감지한 다음 다른 작업을 예약할 수 있도록 작업을 강제로 생성할 수 있습니다. 따라서 관리 코드를 사용하면 확장성과 시스템 리소스 사용 효율이 개선됩니다.

관리 코드를 사용하면 실행 환경을 유지 관리하고 보안 검사를 수행하는 데 필요한 오버헤드가 늘어날 수 있습니다. 예를 들어 SQL Server 내에서 실행되고 관리 코드에서 네이티브 코드로의 수많은 전환이 필요한 경우(SQL Server 네이티브 코드로 전환할 때 스레드별 설정에 대한 추가 유지 관리를 수행해야 하므로). 따라서 확장 저장 프로시저는 관리 코드와 네이티브 코드 간에 빈번한 전환이 있는 경우 SQL Server 내에서 실행되는 관리 코드보다 훨씬 우수할 수 있습니다.

참고

확장 저장 프로시저는 더 이상 사용되지 않는 기능이므로 새로운 확장 저장 프로시저를 개발하지 않는 것이 좋습니다.

사용자 정의 형식에 대한 네이티브 직렬화

UDT(사용자 정의 형식)는 스칼라 형식 시스템을 위한 확장성 메커니즘으로 설계되었습니다. SQL Server Format.Native라는 UDT에 대한 serialization 형식을 구현합니다. 컴파일 중에 형식의 구조를 검사하여 해당하는 특정 형식 정의에 맞게 사용자 지정된 MSIL을 생성합니다.

네이티브 serialization은 SQL Server 기본 구현입니다. 사용자 정의 직렬화는 직렬화 수행을 위해 형식 작성자가 정의한 메서드를 호출합니다. 최상의 성능을 위해 가능한 경우 Format.Native serialization을 사용해야 합니다.

비교할 수 있는 UDT의 정규화

UDT 정렬 및 비교와 같은 관계형 작업은 값의 이진 표현에 대해 직접 수행됩니다. 이러한 작업은 UDT 상태의 정규화된(이진 순서로 정렬된) 표현을 디스크에 저장하는 방식으로 이루어집니다.

정규화를 사용하면 형식 인스턴스의 생성과 메서드 호출 오버헤드를 방지하여 상당히 적은 비용으로 비교 작업을 수행할 수 있고, UDT에 대한 이진 도메인을 생성함으로써 히스토그램, 인덱스 및 형식의 값에 대한 히스토그램을 만들 수 있다는 두 가지 이점이 있습니다. 따라서 메서드 호출이 필요하지 않은 작업에서 정규화된 UDT의 성능 프로필은 네이티브 기본 제공 형식과 매우 비슷합니다.

확장 가능한 메모리 사용

관리되는 가비지 수집이 SQL Server 잘 수행되고 크기가 조정되도록 하려면 대규모 단일 할당을 방지합니다. 88KB보다 큰 할당은 대형 개체 힙에 배치되므로 많은 수의 작은 할당보다 가비지 수집의 성능 및 확장성이 크게 떨어집니다. 예를 들어 대형 다차원 배열을 할당해야 하는 경우에는 분산된 가변 배열로 할당하는 것이 좋습니다.

참고 항목

CLR 사용자 정의 형식