다음을 통해 공유


CLR 통합 아키텍처 - 성능

적용 대상: SQL Server Azure SQL Managed Instance

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

컴파일 프로세스

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

붙이기 코드는 형식별 최적화를 사용하도록 설정하고 Null 허용 여부, 제약 패싯, 값별 및 표준 예외 처리와 같은 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(Open 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의 기본 구현입니다. 사용자 정의 serialization은 형식 작성자가 정의한 메서드를 호출하여 serialization을 수행합니다. 최상의 성능을 위해 가능한 경우 Format.Native serialization을 사용해야 합니다.

비교 가능한 UDT의 정규화

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

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

확장 가능한 메모리 사용량

관리되는 가비지 수집이 SQL Server에서 잘 수행되고 크기를 조정하려면 대규모 단일 할당을 피합니다. 크기가 88KB보다 큰 할당은 큰 개체 힙에 배치되므로 가비지 수집이 더 작은 할당보다 훨씬 더 성능이 저하됩니다. 예를 들어 큰 다차원 배열을 할당해야 하는 경우 들쭉날쭉한(분산된) 배열을 할당하는 것이 좋습니다.

참고 항목

CLR 사용자 정의 형식