2007/07/11 01:50

컴퓨터의 OS가 비스타인 경우Oracle을 설치하려고 시도하면 에러가 납니다.

Oracle사이트의 링크를 거치고 거쳐Oracle Client 10g/ODP.NET 10g설치에 성공을 하여 설치 팁을 알려드릴까 합니다.

아래의 방법으로 시도를 해보세요.

1. Oracle Client를 다운로드 받습니다.(다운로드 후 압축풀기 - 예전에 압축을 푼 경로에 한글이 포함되면 에러가 났었는데 지금은 모르겠네요. 제 경우 한글이 포함이 안되있어서 일부러 포함시켜 보진 못했습니다.)

2. 압축 푼 폴더에서 setup.exe를 실행시키고 설치를 진행합니다.

(옵션 선택은 개개인의 환경에 맞춰서 설치를 하시면 됩니다.)

--여기까지는 무난하게 설치가 됩니다.

 

다음은 ODP.NET 설치입니다.

ODAC라는 이름으로 프로그램 설치 파일이 제공이 되는데요. 최신 버전으로 다운을 받아도 설치시 에러가 납니다.

일단 ODP.NET을 다운로드 받는데요. VS.NET의 add-in 툴을 같이 설치하려면

Oracle Developer Tools for Visual Studio .NET with ODAC 10.2.0.2.21

아니라면ODAC 10.2.0.2.21을 다운로드 받고 다운로드 받은 파일을 실행합니다.

실행하면 다운로드 받은 파일이 위치한 곳에 두개의 폴더(install/stage)가 생성됩니다.

일단 여기까지 했다면 다음은 아래의 방법으로 시도를 해보세요.

 

1. 명령프롬프트창(cmd.exe)를 열고 Oracle이 설치된 경로로 이동합니다.(기본적으로 설치를 하면 D:\oracle\product\10.2.0\client_1\oui\bin 로 이동하시면 됩니다.)

2. 명령프롬프트창에서 다음 명령을 실행합니다. setup.exe use_prereq_checker=false

3. 위 명령을 실행하면 창이 하나 더 열리고 명령 실행 후 Oracle설치 화면이 나옵니다.

4. 경로를 선택하는 부분에서 찾아보기를 클릭하고 위에서 생성된 폴더(ODAC파일 실행후 생성된 폴더) stage로 찾아가 products.xml을 선택하고 설치를 실행하면 에러없이 설치가 됩니다.

 

설치 화면을 캡쳐했으면 좋았을텐데 그러지 못해 아쉽군요. 캡쳐를 위해 제거하고 설치하기는 좀....ㅋ

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari
2006/04/20 21:53

출처 -MSDN

aspnet_merge.exe 명령을 사용하여 배포용으로 미리 컴파일된 ASP.NET 출력 관리

Microsoft Corporation

2005년 11월

소개

ASP.NET 2.0에서는 aspnet_compiler.exe 명령줄 도구를 사용하여 웹 사이트를 미리 컴파일하고, 서버로 배포할 수 있는 웹 사이트 버전을 만들 수 있습니다. 이 도구는 페이지, 사용자 정의 컨트롤, 리소스, 유틸리티 클래스, 기타 파일 등의 소스 파일을 어셈블리로 컴파일합니다.

기본적으로 컴파일러는 여러 소스 파일의 출력이 파일 형식, 파일 종속성 및 기타 조건에 따라 단일 어셈블리로 컴파일되는 "일괄 처리 모드"로 작동합니다. 결과적으로 대상 사이트에는 원래 소스 파일용 실행 코드와 함께 어셈블리 집합이 포함됩니다.

일괄 컴파일로 만든 어셈블리가 프로덕션 서버로 웹 사이트를 배포하는 데 적합하지 않은 경우도 있습니다. 어셈블리 이름은 컴파일러에 의해 자동으로 생성되므로 어떤 어셈블리가 어떤 소스 파일에 매핑되는지 명확히 알 수는 없습니다. 컴파일러가 실행될 때마다 새 이름이 만들어지기 때문에 매 컴파일 후 어셈블리 이름이 다를 수 있습니다. 또한 소스 파일이 변경되면 컴파일러에서 소스 파일을 다른 방식으로 일괄 처리할 수 있습니다. 즉, 결과 어셈블리가 동일한 소스 파일을 나타내지 않을 수 있습니다. 배포된 웹 사이트를 유지 관리하면서 최신 변경 내용이 있을 때만 어셈블리를 업데이트하려는 경우에는 일괄 컴파일의 출력으로 인해 작업이 더 복잡해질 수 있습니다.

이러한 상황에서 편리하게 작업을 진행할 수 있도록 aspnet_compiler.exe에서는 패키지 및 릴리스 관리용으로 특수하게 고안된-fixednames옵션을 지원합니다. 이 옵션을 사용하면 두 가지 이점을 제공하는 컴파일러 출력을 만들 수 있습니다. 첫 번째 이점은 컴파일러로 생성된 어셈블리의 이름이 컴파일할 때마다 동일하다는 것이고, 두 번째는 어셈블리가 매번 동일한 입력 파일을 기반으로 한다는 것입니다.

그러나-fixednames옵션을 사용해도 결과 어셈블리 이름이 여전히 복잡한 경우가 있을 수 있습니다. 더욱이fixednames옵션을 사용할 때 컴파일러는 컴파일된 각 소스 파일에 대해 하나씩, 많은 수의 어셈블리를 만들 수 있습니다.

대규모 웹 사이트에서 작업하는 개발자가 배포 가능한 웹 응용 프로그램을 만들 경우에는 다음과 같은 기능이 필요합니다.

  • 출력 어셈블리 및 파일을 관리하는 기능. Visual Studio .NET 2003에서 빌드 작업을 수행하면 단일 어셈블리가 생성되므로 어셈블리를 비교적 간단히 배포할 수 있습니다. 그러나 웹 UI 콘텐츠 파일(ASP.NET 페이지 및 사용자 정의 컨트롤)은 서버에서 여전히 동적으로 컴파일되며 배포가 필요합니다. Visual Studio .NET 2003에서 빌드 출력을 "단일 어셈블리"라는 용어로 지칭하는 것은 완전히 정확하다고는 할 수 없습니다. 코드 숨김 파일은 단일 어셈블리로 컴파일되지만 페이지 어셈블리는 여전히 동적으로 생성되며 일괄 컴파일되거나 개별적으로 컴파일되기 때문입니다.
  • 자동으로 생성되는 어셈블리 이름을 그대로 사용하지 않고 결과 출력 어셈블리의 이름을 명시적으로 지정하는 기능
  • 릴리스 관리 및 배포 프로세스를 능률화하기 위해 빌드를 보다 쉽게 비교("diff")하는 기능

ASP.NET 2.0 컴파일러에서-fixednames옵션을 사용하면 이러한 기능 중 일부가 지원되기는 하나 사용자는 결과 어셈블리의 이름을 지정할 수 없으며 하나 또는 소수의 어셈블리만 만들 수도 없습니다.

이러한 문제를 해결하기 위해 aspnet_merge.exe라는 새로운 병합 유틸리티가 개발되었습니다. 이 유틸리티를 사용하면 컴파일러에서 생성한 어셈블리를 결합하여 관리할 수 있습니다. 병합 도구와 사용 방법은 이 기사에 설명되어 있습니다.

웹 사이트 예제

컴파일러와 병합 도구가 어떻게 함께 작동하는지 설명하기 위해 Visual Studio 2005에서 Personal Web Starter Kit를 사용하여 웹 사이트를 만들었다고 가정해 봅시다. 이 사이트의 파일 구조는 그림 1에 나와 있습니다.

사용자 삽입 이미지

그림 1. Visual Studio 2005를 사용하여 만든 개인 웹 사이트 예제

사이트 콘텐츠 및 레이아웃에 대한 다음 내용을 참고하십시오.

  • 웹 UI 콘텐츠 파일(.aspx 파일, .master 파일 및 .ascx 컨트롤)은 응용 프로그램의 여러 폴더에 있습니다.
  • 사이트에는 .jpg 및 .css 파일과 같은 정적 콘텐츠가 있습니다.
  • 유틸리티 클래스는 App_Code 폴더에 정의되어 있습니다.

이와 같은 웹 사이트의 일반적인 개발 주기는 다음 단계에 따라 진행됩니다.

  • 디자인 및 개발디자이너와 개발자가 Visual Studio 2005를 사용하여 웹 사이트를 만들고 반복해서 테스트 및 수정 작업을 수행합니다. 이 단계에서 Visual Studio 2005 및 ASP.NET 2.0에서 사용되는 동적 컴파일은 Visual Studio 2003 및 ASP.NET 1.1과 비교할 때 개발 프로세스의 속도를 한층 향상시킵니다.
  • 빌드응용 프로그램을 소스 제어로 체크 인하고 빌드 서버를 통해 동기화할 수 있습니다. 개발자는 사용자 지정 빌드 스크립트를 만들어서 aspnet_compiler.exe 및 aspnet_merge.exe와 기타 도구를 실행할 수 있습니다. 또한 배포 업무를 담당하는 개발자는 이 기사의 뒷부분에 나오는 "Visual Studio 2005 웹 배포 프로젝트" 섹션에 설명된 것처럼 새 웹 배포 프로젝트를 사용하여 MSBuild 프로젝트를 만들 수 있습니다. 빌드 스크립트를 통해 배포 가능한 대상 또는 패키지된 대상을 만들 수 있습니다.
  • 배포스크립트 또는 MSBuild 프로젝트를 통해 준비 또는 프로덕션/릴리스 서버로 이동되는 배포 가능한 웹 사이트를 만들 수도 있습니다.
  • 테스트팀에서 준비 서버를 사용하여 응용 프로그램을 테스트합니다.
  • 프로덕션/릴리스준비된 빌드가 프로덕션/릴리스 서버로 이동됩니다.
  • 유지 관리팀 차원에서 준비 또는 프로덕션/릴리스 서버로 변경 내용을 전파하는 프로세스를 실행합니다.
  • 이 프로세스가 가장 이상적이기는 하나 설치 파일(.msi 파일)을 만들거나 증분 업데이트를 관리하고, 매일 수행한 빌드를 저장하고, 릴리스 관리 프로세스를 구현하는 프로세스를 만드는 등 다양한 방식으로 이 작업을 수행할 수 있습니다. 이러한 모든 시나리오에서 aspnet_compiler.exe 및 aspnet_merge.exe 도구는 웹 사이트 개발 프로세스에서 핵심적인 역할을 합니다. Visual Studio 2005 웹 배포 프로젝트를 사용하면 빌드, 병합 및 배포 프로세스의 여러 측면을 좀 더 향상시킬 수 있습니다.

이 기사에서는 이러한 프로세스에 대해 자세히 설명하지는 않으며, 빌드 서버에서 또는 Visual Studio 2005 웹 배포 프로젝트를 통해 사용할 수 있는 aspnet_compiler.exe는 물론 특히 중요한 aspnet_merge.exe 도구를 중점적으로 다룹니다.

이 기사에 제공된 예제에 대한 참고 사항

이 기사에 제공된 예제에서는 해당 사이트가 IIS의 응용 프로그램으로 구성되어 있다고 가정하고-v옵션을 지정하여 aspnet_compiler.exe를 사용하는 방법을 보여 줍니다. 그러나 컴파일러는-p옵션으로 지정된 실제 경로를 사용할 수 있습니다.-p옵션을 사용하면 aspnet_compiler.exe는 루트 아래의 모든 하위 폴더를 루트 웹 사이트에 속하는 폴더로 취급합니다. 이로 인해 해당 폴더가 독자적인 웹 사이트인 경우 컴파일 오류가 발생할 수 있습니다.-p옵션을 지정하지 않고-v옵션을 사용하면 aspnet_compiler.exe는 어떤 하위 폴더가 루트 응용 프로그램에 속하는지 확인할 수 있습니다.

-p옵션을 사용할 경우-v옵션은~연산자의 위치를 확인하거나 설치 디렉터리 아래의 Temporary ASP.NET Files 폴더에 폴더를 제공하는 등 다양한 용도로 사용됩니다.

모든 예제 명령은 예제 웹 사이트 루트 폴더에서 실행된다고 가정합니다.

미리 컴파일 개요

새 병합 도구를 살펴보기 전에 ASP.NET 2.0 컴파일러(aspnet_compiler.exe)의 기능을 검토하는 것이 도움이 될 수 있습니다. 이 컴파일러는 .NET Framework 버전 2.0과 함께 제공되며 .NET Framework 설치 폴더(일반적으로 %windir%\Microsoft.NET\Framwork\version) 또는 Visual Studio 2005 Professional Edition이나 Visual Studio 2005 Enterprise Edition에서 실행할 수 있습니다.

ASP.NET 컴파일러를 사용하면 웹 응용 프로그램을 여러 배포 형식으로 미리 컴파일할 수 있습니다. 이러한 각 배포 형식은 웹 응용 프로그램에서 소스 코드를 제거합니다. 또한 이 컴파일러는 필요에 따라 웹 UI 콘텐츠 페이지(.aspx, .master 및 .ascx 파일)를 제거할 수 있습니다.

aspnet_compiler.exe 및 aspnet_merge.exe 도구 설명에는 다음 두 어셈블리 그룹이 사용됩니다.

  • 웹 UI 콘텐츠 어셈블리웹 UI 콘텐츠 자체(.aspx, .master 및 .ascx 파일과 관련된 코드 파일)에 대해 생성되는 어셈블리입니다.
  • 최상위 수준 어셈블리App_Code, App_GlobalResources 및 App_WebReferences와 같은 특수 목적 폴더로부터 Global.asax 같은 특수 파일에 대해 컴파일러에서 생성되는 어셈블리입니다.

aspnet_compiler.exe 명령은 소스 사이트를 미러링하는 레이아웃 구조로 이루어진 출력을 생성합니다. 이 경우 컴파일된 개체 코드는 대상 응용 프로그램의 Bin 폴더에 배치됩니다.

이 컴파일러는 배포용 출력 및 현재 위치에서 출력이라는 두 가지 출력 유형을 제공하는 미리 컴파일을 지원합니다.

배포용 미리 컴파일

배포용으로 미리 컴파일을 수행하면 서버에 작업 웹 응용 프로그램으로 배포할 수 있는 파일 집합이 생성됩니다. 배포용으로 미리 컴파일할 경우 다음과 같은 두 가지 유형의 출력을 만들 수 있습니다.

  • 업데이트가 불가능하도록 배포용으로 미리 컴파일은 대상에서 소스 코드뿐만 아니라 모든 태그를 제거합니다. 이 옵션을 사용할 경우 최상위 수준 어셈블리 및 웹 UI 콘텐츠 파일에 대한 모든 소스가 프로덕션 환경에서 제거됩니다. 이러한 특성은 웹 UI 콘텐츠 파일이 항상 동적으로 컴파일되는 ASP.NET 1.1과는 대조됩니다.
  • 업데이트가 가능하도록 배포용으로 미리 컴파일은 웹 UI 콘텐츠 페이지의 태그를 유지하며 Visual Studio .NET 2003에서 생성된 사이트와 비슷한 대상 사이트를 만듭니다.

이 컴파일러의-fixednames옵션은 두 가지 형식의 배포용 미리 컴파일 모두에 영향을 줍니다. 이 옵션을 지정하면 컴파일 중에 특정(고정) 이름을 갖는 어셈블리가 생성되고 웹 UI 콘텐츠 파일마다 단일 어셈블리가 생성됩니다.

현재 위치에서 미리 컴파일

현재 위치에서 미리 컴파일할 경우 소스 사이트에 직접 어셈블리를 만들고 새 대상 레이아웃은 생성하지 않는 일괄 컴파일이 수행됩니다. 현재 위치에서 미리 컴파일하려면 웹 사이트 가상 디렉터리를 참조하는-v옵션을 사용하거나(컴퓨터에 IIS가 설치된 경우) 실제 소스 경로를 참조하는-p옵션을 사용하여 컴파일러를 실행합니다.

여기서는 모든 내용을 빠짐없이 설명하기 위해 현재 위치에서 미리 컴파일 기능을 언급했지만 이 기능을 사용할 경우 빌드 시스템에서 aspnet_merge.exe를 통해 결합할 수 있는 출력이 생성되지 않습니다. 현재 위치에서 미리 컴파일할 경우 모든 소스 코드가 유지되는 프로덕션 웹 사이트를 만들 수 있지만 웹 사이트에 대한 첫 번째 요청으로 인해 야기될 수 있는 "컴파일 패널티"는 나타나지 않습니다.

미리 컴파일 - 코드 및 태그 제거

웹 사이트 예제로 돌아가서 그림 2의 명령을 사용하여 배포용으로 웹 사이트를 미리 컴파일하여 업데이트가 불가능한 출력을 만든다고 가정해 봅시다. 이 명령은 웹 사이트에 대한 psw라는 가상 경로를 사용하고 pswcompile이라는 출력 폴더를 지정합니다.

사용자 삽입 이미지

그림 2. aspnet_compiler.exe 실행 시 -v 옵션 지정

컴파일러는 그림 3과 4처럼 대상 사이트(pswcompile)를 생성합니다. 그림 3에서는 소스 웹 사이트 구조가 유지되는 것을 확인할 수 있습니다. 예를 들어 Admin 폴더는 유지되고 Welcome.html과 같은 기타 정적 웹 UI 콘텐츠는 그대로 pswcompile 폴더에 복사됩니다. .aspx 파일은 복사된 것처럼 보이지만 자세히 살펴보면 이 파일이 실제로는 더미 태그 파일이며 해당 내용은 문자열 값에 불과하다는 것을 알 수 있습니다. 이 경우에는 모든 태그(HTML 및 웹 서버 컨트롤)가 제거되었습니다.

그림 4처럼 대상 웹 사이트에는 소스 파일이 전혀 포함되어 있지 않으며 웹 UI 콘텐츠 어셈블리가 Bin 폴더에 생성되었습니다. aspnet_compiler.exe 명령은 Bin 폴더에 .compiled 파일을 만듭니다. 이 파일은 특정 페이지나 특정 가상 경로에 대한 요청이 있을 때 인스턴스화할 클래스를 ASP.NET에 알려줍니다.

메모장과 같은 텍스트 편집기에서 .compiled 파일을 열면 컴파일된 페이지에 대한 참조가 포함되어 있음을 알 수 있습니다. 이 참조를 통해 ASP.NET에서 파일 구조의 원래 페이지 위치를 결정할 수 있습니다. 또한 .compiled 파일에서 페이지 결과로 컴파일된 출력을 포함하는 어셈블리 이름을 확인할 수 있습니다. 컴파일러는 고유한 어셈블리 이름을 자동으로 생성합니다.

참고배포용으로 미리 컴파일할 경우 항상 새 빌드가 수행되며 aspnet_compiler.exe는 매번 다른 어셈블리 이름을 만듭니다. 또한 어셈블리의 소스로 사용되는 파일은 빌드마다 다를 수 있으므로 릴리스 관리 시 변경에 의해 어떤 어셈블리가 생성되었는지 일관적으로 파악하는 것이 어려울 수 있습니다. 이러한 이유 때문에-fixednames옵션을 사용합니다.

사용자 삽입 이미지

그림 3. pswcompile 폴더의 미리 컴파일된 출력

사용자 삽입 이미지

그림 4. pswcompile\Bin 폴더의 미리 컴파일된 출력

고정 이름을 사용한 미리 컴파일

그림 5처럼-fixednames옵션을 사용하여 웹 사이트를 컴파일한다고 가정해 봅시다.

사용자 삽입 이미지

그림 5. -fixednames 옵션을 사용한 컴파일

-fixednames옵션을 사용하면 그림 6처럼 pswcompile\Bin 폴더에 더 많은 어셈블리가 생성됩니다. 각 어셈블리는 하나의 페이지, 사용자 정의 컨트롤 또는 마스터 페이지를 나타냅니다. 옵션 이름에서도 알 수 있듯이 어셈블리 이름은 빌드에 관계없이 동일합니다. 어셈블리 이름은 App_Web_filetype.nnnn.dll 형식으로 지정됩니다. 여기서 nnnn은 해시 값을 나타냅니다.

사용자 삽입 이미지

그림 6. -fixednames 옵션을 사용하여 컴파일한 후 pswcompile\Bin 폴더에 생성되는 미리 컴파일된 출력

미리 컴파일 - 코드만 제거

배포 가능한 대상 디렉터리(예제의 pswcompile)에 웹 UI 콘텐츠 페이지의 태그가 유지되도록 웹 사이트를 미리 컴파일할 수 있습니다. 그림 7에서는 배포용으로 미리 컴파일된 웹 사이트를 생성하는 aspnet_compiler.exe 명령 예제를 보여 줍니다. 그런 다음 사이트에서 사이트 페이지를 제한적으로 수정할 수 있습니다. 이 경우 레이아웃은 변경할 수 있지만 이벤트 핸들러처럼 코드가 필요한 새 컨트롤이나 컨트롤 ID는 변경할 수 없습니다.

사용자 삽입 이미지

그림 7. aspnet_compiler.exe 실행 시 ASP.NET 페이지 및 사용자 정의 컨트롤의 태그 유지

그림 8과 9에서는 pswcompile 폴더와 Bin 폴더의 결과를 보여 줍니다.

사용자 삽입 이미지

그림 8. pswcompile 폴더의 미리 컴파일된 출력

사용자 삽입 이미지

그림 9. pswcompile\Bin 폴더의 미리 컴파일된 출력

그림 8에서는 앞에 나온 컴파일처럼 웹 사이트 구조가 유지되고 Welcome.html과 같은 정적 웹 UI 콘텐츠가 pswcompile 폴더에 복사되었음을 보여 줍니다. 그림 9에서는 웹 UI 콘텐츠 어셈블리와 App_Code.dll 같은 최상위 수준 어셈블리가 pswcompile\Bin 폴더에 생성되었음을 보여 줍니다.

웹 UI 콘텐츠 페이지를 확인해 보면 이 미리 컴파일 형식의 차이점을 알 수 있습니다. 그림 9를 보면 pswcompile\Bin 폴더에 .compiled 파일이 더 포함되어 있지 않다는 것을 알 수 있습니다. 이것은 웹 UI 콘텐츠 페이지 자체가 유지되기 때문입니다. .aspx 페이지는 앞에 나온 컴파일과 같이 더미 파일로 작동하지 않으며 그림 10처럼 다른 어셈블리에서 상속하도록 수정됩니다. 미리 컴파일 후에 이 페이지는 Bin 폴더에 있는 어셈블리에서 상속하며 이전codeFile참조는 모두 제거됩니다.

사용자 삽입 이미지

그림 10. 미리 컴파일된 .aspx 파일의 수정된 inherits 특성

고정 이름을 사용한 미리 컴파일

그림 11의 구문을 사용하면 태그를 유지하고 고정 이름을 사용하도록 사이트를 미리 컴파일할 수 있습니다. 앞의 경우처럼-fixednames옵션을 사용하면 Bin 디렉터리에 더 많은 어셈블리가 생성됩니다.

사용자 삽입 이미지

그림 11. aspnet_compiler.exe 실행 시 웹 UI 콘텐츠 태그를 유지하고 고정 어셈블리 이름 사용

aspnet_merge.exe를 사용하여 어셈블리 병합

앞에서 살펴보았듯이 aspnet_compiler.exe는 기본적으로 일괄 처리 모드로 출력 어셈블리를 생성하거나 컴파일된 각 파일에 대해 고정 이름을 갖는 어셈블리를 만들어 출력 어셈블리를 생성합니다. 이러한 경우 배포 및 릴리스 관리 계획과 웹 사이트의 특징에 따라 엔터프라이즈 개발자에게 몇 가지 이점이 있습니다.

그러나 출력이 너무 많거나 어셈블리의 이름이 작업 용도에 맞게 지정되지 않는 경우도 있습니다. aspnet_merge.exe 명령을 사용하여 이러한 문제를 보다 쉽게 해결할 수 있습니다.

aspnet_merge.exe 도구는 컴파일러에서 생성된 어셈블리를 결합합니다. 병합 도구를 사용하면 다음과 같이 어셈블리를 결합할 수 있습니다.

  • 사전 컴파일된 웹 사이트에서 ASP.NET에 의해 생성된 모든 어셈블리(사용자 지정 어셈블리 제외)를 단일 명명 어셈블리로 결합할 수 있습니다.
  • 모든 웹 UI 콘텐츠 어셈블리를 단일 명명 어셈블리로 결합할 수 있습니다.
  • 웹 UI 콘텐츠 어셈블리를 웹 사이트의 각 폴더에 대한 어셈블리로 결합할 수 있습니다.
참고Visual Studio .NET 2003에서 프로젝트를 빌드하면 코드 숨김 파일과 기타 클래스 파일이 웹 사이트용 어셈블리로 빌드됩니다. 소스 코드를 배포하지 않고도 이 어셈블리를 프로덕션 서버로 배포할 수 있습니다. 프로덕션 서버로 모든 태그 페이지도 배포해야 합니다. 증분 업데이트를 사용하여 사이트를 업데이트하려면 코드 숨김 어셈블리 및 관련 태그 페이지를 다시 배포합니다. 그러나 단일 어셈블리에는 단점이 있습니다. 어셈블리를 빌드하는 데는 시간이 많이 걸리므로 배포 중에 F5 키나 Ctrl+F5를 누르면 시간이 많이 지연될 수 있습니다. 뿐만 아니라 코드 숨김 클래스 파일을 변경한 경우에는 전체 어셈블리를 다시 빌드해야 합니다. 테스트를 수행할 경우 새 어셈블리를 만들면 웹 사이트의 모든 페이지가 기술적으로 무효화되므로 모든 페이지를 다시 테스트해야 할 수 있습니다.

이제 ASP.NET 2.0에서는 동적 컴파일, aspnet_compiler.exe를 사용한 미리 컴파일 및 aspnet_merge.exe를 사용한 병합을 조합하여 개발 및 배포 요구를 최대한 충족시킬 수 있습니다. 다음 표에서는 각 컴파일 유형 및 병합 유틸리티를 사용해야 하는 경우를 보여 줍니다.

시나리오참고 사항
개발 중: 동적 컴파일 사용Visual Studio 2005 및 ASP.NET 2.0의 동적 컴파일을 사용합니다.

이렇게 하면 F5 키나 Ctrl+F5를 누를 때 전체 빌드가 수행되지 않도록 프로젝트를 구성할 수 있으므로 개발 작업을 빠르게 진행할 수 있습니다. 실제로 빌드를 완전히 비활성화할 수도 있습니다. 이러한 경우 Visual Studio 2005에서 작업 중인 페이지를 실행할 때 페이지와 해당 종속성이 ASP.NET 2.0에 의해 동적으로 컴파일됩니다. 따라서 특정 페이지에 오류가 있어도 다른 페이지를 개발하고 디버깅할 수 있습니다.

Visual Studio 2005에서는 ASP.NET 컴파일을 사용하므로 IDE의 모든 코드 오류와 구문 분석 시간 오류를 비롯하여 Visual Studio 컴파일과 ASP.NET 컴파일이 정확하게 일치합니다. 또한 ASP.NET 2.0 동적 컴파일은 편집기에서 사용자 지정 IntelliSense 기능을 제공합니다.
배포: 각 웹 UI 콘텐츠 파일에 대한 단일 어셈블리 만들기-fixednames옵션으로 미리 컴파일하여 각 파일에 대한 웹 UI 콘텐츠 어셈블리가 있는 대상을 생성합니다.

이 시나리오에서는 다른 페이지에 영향을 거의 주지 않고 웹 페이지 수준까지 증분 업데이트를 수행할 수 있으므로 보다 세분화된 릴리스 관리가 가능합니다. 이 옵션을 사용하면 업데이트 가능한 미리 컴파일된 웹 사이트와 업데이트 불가능한 미리 컴파일된 웹 사이트를 모두 만들 수 있습니다.

대규모 사이트의 경우 많은 어셈블리가 생성되므로 확장성 문제가 발생할 수 있습니다.

어셈블리는 암호화 방식으로 명명되지만 페이지와 .compiled 파일을 함께 사용하면 어떤 파일이 어떤 어셈블리를 생성하는지 파악할 수 있습니다.

정적 웹 콘텐츠 및 사용자 지정 어셈블리는 영향을 받지 않습니다.
배포: 각 웹 UI 콘텐츠 폴더에 대한 어셈블리 만들기미리 컴파일을 하고 aspnet_merge.exe를 사용하여 웹 UI 콘텐츠가 들어 있는 각 폴더에 대해 별도의 어셈블리를 만듭니다.

웹 UI 콘텐츠 폴더별로 어셈블리를 따로 만들면 어셈블리 수가 많아집니다. 컴파일할 때-u옵션을 생략하면 어셈블리와 더미 페이지만 배포하는 웹 사이트를 만들어 배포된 웹 사이트에서 태그가 제거되도록 할 수 있습니다.

폴더 내의 파일을 변경한 경우 폴더 어셈블리와 수정된 페이지를 다시 배포해야 합니다. 해당 폴더에 대해서만 테스트 프로세스를 수행할 수 있으면 다른 폴더는 여전히 유효하다고 간주해도 됩니다. 그러나 실제로 이 웹 사이트에 태그 페이지가 유지될 경우 Bin 폴더에 새 어셈블리를 추가하면 모든 페이지가 다시 컴파일됩니다.

기본적으로 어셈블리 이름은 폴더 이름을 기반으로 하지만 어셈블리에 사용자 지정 이름을 접두사로 붙일 수 있습니다. 최상위 수준 어셈블리는 이 옵션을 사용해도 영향을 받지 않으며 정적 웹 UI 콘텐츠 및 사용자 지정 어셈블리에도 영향이 없습니다.

배포: 모든 웹 UI 콘텐츠 파일에 대한 단일 어셈블리 만들기사이트를 미리 컴파일한 후 aspnet_merge.exe를 사용하여 전체 웹 사이트에 대한 단일 웹 UI 콘텐츠 어셈블리를 생성합니다.

웹 UI 콘텐츠에 대한 단일 어셈블리가 있으면 Visual Studio .NET 2003에서 생성된 출력과 비슷한 배포 가능한 웹 사이트를 만들 수 있습니다. 컴파일할 때-u옵션을 생략하면 옵션을 생략하면 어셈블리와 더미 페이지만 배포하는 웹 사이트를 만들어 배포된 웹 사이트에서 태그가 제거되도록 할 수 있습니다.

배포된 웹 사이트를 업데이트하려는 경우 다시 컴파일한 어셈블리와 수정된 웹 UI 콘텐츠 파일을 배포할 수 있습니다.

웹 UI 콘텐츠 파일을 변경할 경우 관련 .compiled 페이지 및 .aspx 페이지와 함께 전체 어셈블리를 다시 배포해야 합니다. 프로덕션 서버에 태그 페이지는 유지되지만 동적으로 다시 컴파일되므로 해당 페이지에 대한 테스트 프로세스 결과가 무효화될 수 있습니다.

병합 프로세스 중에 어셈블리에 사용자 지정 이름을 할당할 수 있습니다. 최상위 수준 어셈블리는 이 옵션을 사용해도 영향을 받지 않으며 정적 웹 UI 콘텐츠 및 사용자 지정 어셈블리에도 영향이 없습니다.
배포: 전체 웹 사이트에 대한 어셈블리 만들기미리 컴파일을 하고 aspnet_merge.exe를 사용하여 웹 UI 콘텐츠 어셈블리와 최상위 수준 어셈블리를 결합하는 단일 어셈블리를 생성합니다.

전체 웹 사이트에 대한 단일 어셈블리를 만들어 릴리스 프로세스를 쉽게 관리할 수 있습니다. 단일 어셈블리와 업데이트된 웹 UI 콘텐츠 파일을 배포할 수 있습니다. 컴파일할 때-u옵션을 생략하면 어셈블리와 더미 페이지만 포함하는 웹 사이트를 만들고 배포된 웹 사이트에서 태그가 제거되도록 할 수 있습니다.

웹 사이트의 파일을 변경한 경우 어셈블리 및 더미 페이지까지 다시 배포해야 하므로 전체 웹 사이트에 대한 테스트 프로세스 결과가 무효화될 수 있습니다.

어셈블리에 사용자 지정 이름을 할당할 수 있습니다. 정적 웹 UI 콘텐츠 및 사용자 지정 어셈블리는 영향을 받지 않습니다.

Aspnet_merge.exe는 미리 컴파일된 웹 사이트의 어셈블리만 병합하며 미리 컴파일된 알려진 어셈블리에만 사용할 수 있습니다. 이 도구는 사용자 지정 어셈블리나 Bin 폴더에 있는 App_licenses.dll 어셈블리는 수정하지 않습니다.

참고Aspnet_merge.exe는 현재 위치에서 병합을 수행합니다. 미리 컴파일된 웹 사이트는 수정되고 결합된 어셈블리는 제거됩니다. 소스 파일에 대해 aspnet_compiler.exe를 다시 실행하는 것이 여의치 않으면 병합 전에 미리 컴파일된 사이트를 백업하십시오.

각 폴더에 대한 콘텐츠 어셈블리 병합

대상 폴더 이름만 지정하고 다른 옵션 없이 aspnet_merge.exe를 실행하면 각 웹 UI 콘텐츠 폴더에 대한 출력 어셈블리가 생성됩니다. 이 병합 유형의 구문은 그림 12에 나와 있습니다.

사용자 삽입 이미지

그림 12. 옵션 없이 aspnet_merge.exe 실행

이 기본 병합 작업을 수행하면 컴파일러-fixednames옵션에 의해 생성된 어셈블리 수보다 적은 어셈블리가 생성됩니다. 이러한 형식의 병합은 프로덕션 서버에 증분 업데이트를 배포하려는 경우에 유용할 수 있습니다.

업데이트 불가능한 미리 컴파일된 웹 사이트에서 병합

그림 13에서는 업데이트 불가능한 미리 컴파일된 사이트에 대해 옵션을 지정하지 않고 aspnet_merge.exe를 실행할 경우의 결과를 보여 줍니다. 이 그림에서는 그림 2의 미리 컴파일된 출력을 병합한 결과를 보여 줍니다. 어셈블리 이름은 폴더 이름(App_Web_filetype.nnnn.dll)을 포함하도록 다시 지정됩니다. 또한 웹 사이트의 폴더를 병합하면 Root.dll 및 Admin.dll(빨간색 원으로 표시)이라는 어셈블리 두 개가 생성됩니다. 이 두 어셈블리는 각각 루트 폴더의 웹 UI 콘텐츠와 psw\Admin 하위 폴더의 웹 UI 콘텐츠에서 생성된 것입니다. Bin 폴더에는 App_Code.dll과 같은 최상위 수준 어셈블리가 계속 포함되어 있는 상태이며 테마도 컴파일된 후 단일 Themes.dll 어셈블리(파란색 원으로 표시)에 병합되었습니다.

aspnet_compiler.exe 명령의-fixednames옵션 지정 여부와는 관계없이 생성된 어셈블리에 Aspnet_merge.exe를 사용할 수 있습니다. 이 도구는 특정 폴더에 대한 어셈블리 병합을 자동으로 처리합니다.

사용자 삽입 이미지

그림 13. 업데이트 불가능한 미리 컴파일된 웹 사이트의 웹 UI 콘텐츠 폴더를 병합한 결과

업데이트 불가능한 미리 컴파일된 웹 사이트의 경우 컴파일러는 .aspx 파일에 대해 .compiled 파일을 만듭니다. ASP.NET은 .compiled 파일을 사용하여 웹 요청에 맞는 유형을 인스턴스화합니다. 병합 도구는 새로 병합된 어셈블리가 대신 참조되도록 이러한 파일을 업데이트합니다. 그림 14에서는 병합 후 .compiled 파일이 어떻게 달라졌는지 보여 줍니다.

사용자 삽입 이미지

그림 14. 병합 작업 이후 수정된 .compiled 파일

병합된 어셈블리를 식별하는 데는 폴더 이름이 사용되므로 웹 사이트의 웹 UI 콘텐츠 파일 집합에 대한 어셈블리를 비교적 쉽게 확인할 수 있습니다. 그러나 사용자가 원하는 명명 규칙을 사용하려는 경우도 있을 수 있으므로 aspnet_merge.exe로 그림 15처럼-prefix옵션을 사용하여 이러한 어셈블리에 대해 접두사를 지정할 수 있습니다.

사용자 삽입 이미지

그림 15. aspnet_merge.exe 실행 시 -prefix 옵션 지정

그림 16 에서는-prefix옵션으로 병합을 수행하여 생성된 어셈블리를 보여 줍니다. Root.dll은 Contoso.dll로, Admin.dll은 Contoso.Admin.dll로 이름이 바뀌었음을 알 수 있습니다.

사용자 삽입 이미지

그림 16. -prefix 옵션을 지정하여 업데이트 불가능한 미리 컴파일된 웹 사이트를 병합한 결과

참고로컬 리소스가 있는 웹 사이트에서 로컬 리소스는 일반적으로 웹 UI 콘텐츠로 취급됩니다. 그러나 병합 도구는 로컬 리소스를 처리하지 않습니다. 로컬 리소스는 이미 폴더가 지정되어 있기 때문입니다.

업데이트 가능한 미리 컴파일된 웹 사이트에서 병합

업데이트 가능한 미리 컴파일된 웹 사이트의 어셈블리를 병합할 경우 업데이트 불가능한 웹 사이트에서 병합했을 때와 동일한 병합된 출력이 생성됩니다. 단, aspnet_merge.exe는 병합된 어셈블리를 가리키도록 .aspx 페이지 같은 태그 페이지를 수정한다는 점이 다릅니다. 업데이트 불가능한 사이트와 마찬가지로 각 폴더에 대해 어셈블리가 생성되며 병합 도구는 동일한 명명 규칙을 적용합니다. 그림 17에서는 병합 후 예제 사이트의 Bin 폴더를 보여 줍니다. 이 폴더에는 웹 UI 콘텐츠에 대한 어셈블리인 Root.dll(루트 psw 폴더의 웹 UI 콘텐츠에서 생성)과 Admin.dll(psw\Admin 하위 폴더의 웹 UI 콘텐츠에서 생성)이 있습니다.

사용자 삽입 이미지

그림 17. 업데이트 가능한 미리 컴파일된 웹 사이트의 웹 UI 콘텐츠 폴더를 병합한 결과

그러나 업데이트 가능한 미리 컴파일된 웹 사이트에서 병합 기능을 사용할 경우에는 약간의 차이가 있습니다. 업데이트 가능한 미리 컴파일된 웹 사이트에서는 테마가 컴파일되지 않으므로 테마는 원래 상태로 유지됩니다. 마찬가지로 로컬 리소스도 웹 UI 콘텐츠로 간주되어 수정될 수 있으므로 원래 상태로 유지됩니다. 컴파일러는 .aspx, .master 및 .ascx 파일에 대해서는 .compiled 파일을 생성하지 않습니다. 이러한 파일은 응용 프로그램에 그대로 남아 있지만 그림 18처럼 병합 과정에서 새로 병합된 어셈블리를 가리키도록 수정됩니다.

사용자 삽입 이미지

그림 18. 병합 작업 이후 수정된 .aspx 파일

미리 컴파일된 웹 UI 콘텐츠를 단일 어셈블리로 병합

각 웹 UI 콘텐츠 폴더에 대해 개별적으로 어셈블리를 배포하지 않고 aspnet_merge.exe를 사용하여 모든 웹 UI 콘텐츠에 대한 단일 어셈블리를 만들 수 있습니다. 이 경우 웹 UI 콘텐츠에 대한 어셈블리를 하나의 단위로 관리할 수 있고 병합된 어셈블리의 이름을 지정할 수도 있습니다. 그림 19에서는 모든 웹 UI 콘텐츠에 대한 단일 어셈블리를 만드는 구문을 보여 줍니다.

사용자 삽입 이미지

그림 19. aspnet_merge.exe 실행 시 모든 웹 UI 콘텐츠에 대한 단일 어셈블리 만들기

그림 19의 명령은 모든 웹 UI 콘텐츠에 대해 Contoso.dll이라는 단일 어셈블리를 생성하여 Bin 폴더에 넣습니다. 최상위 수준 어셈블리는 그대로 유지됩니다. 모든 .compiled 파일 또는 모든 .aspx, .master 및 .ascx 파일은 이 단일 어셈블리를 참조하도록 수정됩니다.

그림 20에서는 업데이트 불가능한 미리 컴파일된 웹 사이트에서 웹 UI 콘텐츠를 단일 어셈블리로 병합할 경우 pswcompile\Bin 폴더의 변경된 내용을 보여 줍니다.

사용자 삽입 이미지

그림 20. 업데이트 불가능한 미리 컴파일된 웹 사이트의 웹 UI 콘텐츠 폴더를 병합할 경우 pswcompile\Bin 폴더의 변경된 내용

미리 컴파일된 웹 UI 콘텐츠 및 최상위 수준 어셈블리 결합

지금까지 웹 UI 콘텐츠 어셈블리의 병합 결과를 살펴보았습니다. 그러나 aspnet_compiler.exe는 웹 사이트의 일부로 여러 다른 어셈블리를 생성합니다. 이 중에는 App_Code 폴더 및 기타 특수 폴더의 내용을 컴파일할 때 생성되는 최상위 수준 어셈블리가 있습니다.

미리 컴파일된 웹 사이트에서 이러한 어셈블리의 이름에는 소스 폴더의 이름이 사용됩니다. 즉, 어셈블리가 이미 폴더 수준으로 컴파일되었음을 의미합니다. 파일이 변경되면 이러한 어셈블리에도 영향이 있으므로 웹 UI 콘텐츠 파일이 변경되지 않았어도 다시 컴파일해야 합니다. 릴리스 관리 프로세스에서는 이 점을 주의해야 합니다.

Aspnet_merge.exe를 사용하면 최상위 수준 어셈블리와 웹 UI 콘텐츠 어셈블리를 결합하는 단일 어셈블리로 최상위 수준 어셈블리를 병합할 수 있습니다. 웹 사이트에 대해 병합된 단일 어셈블리를 만들려면 병합 명령에-o옵션을 추가하고 어셈블리 이름을 지정하십시오. 그림 21에서는 구문의 예를 보여 줍니다.

사용자 삽입 이미지

그림 21. aspnet_merge.exe 실행 시 최상위 수준 및 웹 UI 콘텐츠 어셈블리에서 단일 어셈블리 만들기

그림 22에서는 그림 2의 파일 작업 시 -o옵션을 지정하여 병합할 경우의 결과를 보여 줍니다. 이러한 유형의 병합에서 aspnet_merge.exe를 실행할 때는 단일 파일의 이름을 지정하는 데 사용되는 출력 어셈블리 이름(Contoso.dll)을 지정해야 합니다. 이 병합 작업에서 다른 어셈블리는 생성되지 않습니다.

사용자 삽입 이미지

그림 22. -o 옵션을 사용하여 모든 어셈블리를 병합한 결과

원본 사이트에 로컬 리소스가 포함되어 있을 경우 큰 리소스 어셈블리로 로컬 리소스를 병합할 수 없으므로 병합이 진행되어도 로컬 리소스는 그대로 남아 있습니다. 마찬가지로 전역 리소스도 병합되지 않습니다.

병합된 어셈블리의 어셈블리 특성

aspnet_merge.exe를 사용하여 어셈블리를 결합할 때 기본적으로 이 도구는 병합된 집합의 초기 소스 어셈블리의 어셈블리 특성을 병합된 최종 어셈블리로 전달합니다. 소스 어셈블리에 다른 특성을 지정할 수도 있으므로 병합된 어셈블리에 어떤 특성이 적용되었는지 확인하는 것은 쉽지 않습니다.

병합된 어셈블리에 지정된 특성을 명확히 알 수 있도록 App_Code 어셈블리에 대해 정의된 어셈블리 특성을 소스 특성 집합으로 사용하거나 지정된 어셈블리를 특성 정의 어셈블리로 사용하도록 aspnet_merge.exe에 설정할 수 있습니다.

예를 들어 그림 23에서는 그림 1에 나와 있는 웹 사이트의 App_Code 디렉터리에 추가할 수 있는 Assemblyinfo.cs 파일을 보여 줍니다.

사용자 삽입 이미지

그림 23. App_Code 폴더에 추가된 Assemblyinfo.cs 파일

어셈블리를 병합하지 않으면 App_Code 어셈블리에만 이러한 특성이 지정됩니다. Ildasm.exe와 같은 도구를 사용하여 어셈블리의 특성을 확인할 수 있습니다. App_Code 어셈블리에 대해 정의된 특성을 병합된 어셈블리에 지정하려면 그림 24처럼-copyattrs옵션을 사용합니다.

사용자 삽입 이미지

그림 24. aspnet_merge.exe 실행 시 App_Code 어셈블리에 대해 정의된 특성을 병합된 최종 어셈블리에 사용

-copyattrs옵션을 지정하면 최상위 수준 어셈블리인 App_Code.dll이 병합에 포함되지 않은 경우에도 App_Code 어셈블리에 대한 특성이 사용됩니다. 웹 UI 콘텐츠 어셈블리만 병합하는 경우가 이러한 경우에 해당됩니다. 다음 목록에서는 그림 23의 명령으로 생성된 Contoso.dll 어셈블리에 대한 IL의 매니페스트 조각을 보여 줍니다.

.assembly Contoso { .... AssemblyFileVersionAttribute:.. = // ...1.0.4567.0.. .... AssemblyDescriptionAttribute:.. = // ...Contoso 웹 사이트.. .... AssemblyProductAttribute:.. = // ...Contoso.Web.. .... AssemblyCompanyAttribute:.. = // ...Contoso.. .. .ver 1:4000:0:0 }

App_Code 어셈블리를 제외한 다른 어셈블리의 특성을 지정하려면-copyattrs옵션으로 어셈블리 이름을 제공합니다. 이 옵션으로 AssemblyInfo.cs 또는 AssemblyInfo.vb 파일에서 어셈블리를 만들어 병합된 어셈블리에 특성을 지정하는 데만 사용할 수 있습니다.

병합된 어셈블리에 서명

개발 팀의 경우 다음과 같은 이유로 미리 컴파일된 응용 프로그램에 서명하려고 할 수 있습니다.

  • 특정 개발 팀에서 어셈블리가 제작되었음을 확인할 수 있게 하려는 경우. 서명이나 키는 해당 조직에서만 사용할 수 있으므로 이를 통해 어셈블리가 특정 회사에서 제작되었는지에 대해 어느 정도 신뢰성을 부여할 수 있습니다.
  • 알려진 키로 서명된 어셈블리만 신뢰할 수 있도록 하고, 인식할 수 없는 키로 서명되었거나 키가 없는 어셈블리에 대한 실행 권한은 허용하지 않도록 프로덕션 컴퓨터를 잠그려는 경우

병합되고 서명된 웹 사이트 어셈블리를 응용 프로그램 간에 공유하도록 GAC에 추가할 수 있습니다. 그러나 이렇게 할 경우 이미지 같은 리소스 URL 기준 지정 시 문제가 발생할 수 있습니다. 일반적으로 GAC에 미리 컴파일되었거나 병합된 어셈블리를 추가하지 않는 것이 좋습니다. 외부 리소스를 참조하지 않는 사용자 정의 컨트롤에서 생성된 어셈블리와 같이 일부 제한된 상황에서만 병합 및 서명된 어셈블리를 GAC에 추가할 수 있습니다.

aspnet_compiler.exe를 사용하여 미리 컴파일된 사이트의 어셈블리에 서명할 수 있습니다. 그러나 어셈블리를 병합할 경우에는 병합 도구를 사용하여 어셈블리에 서명해야 합니다.

미리 컴파일된 웹 사이트를 병합할 경우 어셈블리의 서명이 연기되었거나 aspnet_compiler.exe를 사용하여 서명되었을 수 있습니다. 병합 중에 미리 서명된 비트 또는 서명되지 않은 비트를 사용하여 병합된 어셈블리에 서명할 수 있습니다. 병합된 결과 어셈블리에 서명하려는 경우 aspnet_merge.exe와 함께 옵션-keyfile,-keyContainer또는-delaysign을 사용할 수 있습니다. aspnet_merge.exe를 실행하여 미리 서명된 어셈블리를 병합할 때 이러한 서명 옵션 중 어느 것도 지정하지 않으면 결과 어셈블리가 서명되지 않습니다.

다음 명령은 미리 컴파일된 어셈블리를 Contoso.dll이라는 단일 어셈블리로 병합한 후 Aspnet_merge.exe를 사용하여 어셈블리에 서명하는 방법을 보여 줍니다.

  1. 공개 키 및 개인 키를 사용하여 키 파일을 만듭니다. sn -k contoso.snk
  2. 서명하지 않고 웹 사이트를 미리 컴파일합니다. aspnet_compiler -v psw pswcompile
  3. 웹 응용 프로그램을 병합하고 서명합니다. aspnet_merge pswcompile -keyfile contoso.snk -o Contoso

병합된 어셈블리 서명 연기

Aspnet_merge.exe를 사용하여 서명을 연기하는 방법은 다음과 같습니다.

  1. 공개 키와 개인 키를 사용하여 키 파일을 만듭니다. 일반적으로 공개 키와 개인 키는 제한된 사용자만 액세스할 수 있는 안전한 위치에 보관해야 합니다. 구문은 다음과 같습니다. sn -k contoso.snk
  2. 해당 파일의 공개 키를 다른 키 파일로 추출합니다. sn -p contoso.snk contosopublicKey.snk
  3. 서명하지 않고 웹 사이트를 미리 컴파일합니다. aspnet_compiler -v psw pswcompile
  4. -delaysign-keyfile옵션을 지정하여 어셈블리를 병합한 후 서명을 연기합니다. aspnet_merge pswcompile -delaysign -keyfile contosopublicKey.snk -o
    Contoso

병합된 어셈블리에 APTCA 적용

미리 컴파일한 웹 사이트가 완전 신뢰 이외의 보안 수준에서 작동하면 aspnet_compile.exe에-aptca(Allow Partially Trusted Callers Attribute) 옵션을 지정하여 어셈블리를 미리 컴파일해야 합니다. 이렇게 하면 해당하는 어셈블리 수준의 특성이 컴파일된 어셈블리에 추가됩니다. 이 특성은 aspnet_merge.exe에 의해 병합된 어셈블리에 유지됩니다.

참고소스 어셈블리의 일부에만 APTCA(AllowPartiallyTrustedCallersAttribute)가 지정된 경우 aspnet_merge.exe는 소스 어셈블리 특성을 전달하지 않고 대신 예외를 발생시킵니다. 따라서 병합된 어셈블리의 코드가 원래 의도된 것과 다른 신뢰 수준으로 제공되는 경우는 없습니다. 단순히 병합하거나-a옵션을 지정하여 병합하기 전에 모든 어셈블리에 APTCA를 지정하여 이 동작을 바꿀 수 있습니다. 그러나 병합할 때-a옵션을 사용하면 이전에는 APTCA가 지정되지 않았던 어셈블리에 이 특성이 지정될 수 있습니다. 따라서 이전에는 가능하지 않았지만 이제는 부분적으로 신뢰할 수 있는 코드에서 어셈블리를 호출하도록 할 수 있습니다.

APTCA를 적용하는 단계는 다음과 같습니다.

  1. -aptca옵션을 지정하여 사이트를 미리 컴파일합니다. aspnet_compiler -v psw pswcompile -aptca
  2. 어셈블리를 병합합니다. aspnet_merge pswcompile -o Contoso

병합된 어셈블리 버전 관리

미리 컴파일된 웹 사이트에 대해 생성된 어셈블리에 버전 정보를 추가하려는 경우 다음을 수행할 수 있습니다.

  • App_Code 폴더에 AssemblyInfo.cs 파일이나 AssemblyInfo.vb 파일을 추가합니다. 이렇게 하면 App_Code 폴더에 대해 생성된 어셈블리의 버전이 관리됩니다. 또한 페이지 및 사용자 정의 컨트롤에 대한 코드 숨김 파일에도 버전 특성을 추가할 수 있습니다. 그러나 개별 파일에 일일이 버전 특성을 추가하는 것은 지루한 작업이므로 업데이트 가능한 미리 컴파일 레이아웃의 코드 숨김 파일에만 버전 특성을 추가합니다.
  • Web.config 파일에 포함된<compiler>요소의compilerOptions특성에 AssemblyInfo.cs 또는 AssemblyInfo.vb 파일에 대한 참조를 추가합니다. 이렇게 하면 웹 사이트에 대해 생성된 모든 어셈블리의 버전 관리 정보를 추가할 수 있습니다. 이 경우 ASP.NET에서 WSDL 같은 비코드 유형 파일에 대한 컴파일러를 선택할 수 있으므로 프록시를 컴파일할 때 C# 컴파일러와 Visual Basic 컴파일러를 모두 포함시켜야 합니다.

Aspnet_merge.exe는 App_Code 어셈블리에서 특성을 복사하거나-copyattrs옵션으로 지정한 특정 어셈블리에서 특성을 복사하여, 만드는 어셈블리의 버전을 관리할 수 있습니다. 따라서 이러한 특성은 다른 어셈블리 특성과 마찬가지로 취급됩니다. 병합된 어셈블리에 대한 특성을 지정하는 데 이러한 옵션을 사용할 경우 파일 버전, 어셈블리 버전 및 작업에 필요한 기타 어셈블리 특성을 비롯한 모든 버전 특성을 정의할 수 있습니다.

그림 25에서는 그림 1의 웹 사이트를 사용하여 App_Code 어셈블리의 특성을 적용하는 데 사용되는 병합 명령을 보여 줍니다. 그림 22의 AssemblyInfo.cs 파일은 웹 사이트의 App_Code 폴더에 추가되었습니다.

사용자 삽입 이미지

그림 25. aspnet_merge.exe 실행 시 App_Code 어셈블리에 대해 정의된 특성을 병합된 최종 어셈블리에 사용

다음 목록에서는 앞의 명령을 실행할 때 Contoso.dll에 적용되는 전체 메타데이터 중 일부를 보여 줍니다.

.assembly Contoso { .... AssemblyFileVersionAttribute:.. = // ...1.0.4567.. .... AssemblyDescriptionAttribute:.. = // ..!Contoso Personal Web // 사이트 시작 키트.. .... AssemblyProductAttribute:.. = // ...Contoso.Web.. .... AssemblyCompanyAttribute:.. = // ...Contoso.. .. .ver 1:0:4000:0

.dll 파일에 대한 속성을 살펴보면 적용된 특성을 알 수 있습니다.

사용자 삽입 이미지

그림 26. Contoso.dll 어셈블리 특성 및 속성

병합된 어셈블리에 대한 디버그 출력 만들기

컴파일 중에 디버그 출력 파일(.pdb 파일)을 만들 수 있습니다. 기본적으로 aspnet_compiler.exe는 정식 버전 출력을 만들고 Web.config 파일이나 .aspx 페이지, master 페이지 또는 사용자 정의 컨트롤에 포함되어 있는 다른 디버그 옵션은 무시합니다. 디버그 출력을 만들려면 그림 27처럼 컴파일할 때-d옵션을 사용합니다.

사용자 삽입 이미지

그림 27. aspnet_compiler.exe 실행 시 -d 옵션을 지정하여 디버그 출력 만들기

디버그 출력 파일을 병합하려면 aspnet_merge.exe 실행 시-debug옵션을 지정합니다.-debug옵션을 지정하지 않으면 병합 과정에서 웹 사이트의 모든 디버그 출력이 제거됩니다. 디버그 출력을 포함하는 사이트를 병합하고-o옵션을 사용하여 단일 어셈블리를 만들 경우 pswcompile\Bin 폴더에 병합된 단일 .pdb 파일이 포함됨을 알 수 있습니다. 그림 28에서는 디버그 출력을 만들기 위한 aspnet_merge.exe 명령을 보여 주고, 그림 29에서는 Bin 폴더에 포함되는 결과 출력을 보여 줍니다.

사용자 삽입 이미지

그림 28. aspnet_merge.exe 실행 시 전체 사이트에 대한 디버그 출력 병합

사용자 삽입 이미지

그림 29. 전체 웹 사이트에 대한 디버그 출력을 병합한 결과

빌드 환경에서 aspnet_compiler.exe 및 aspnet_merge.exe 사용

Visual Studio 2005 Professional Edition 및 Visual Studio 2005 Enterprise Edition에는 미리 컴파일을 수행하기 위한 메뉴 명령(빌드>웹 게시)이 포함되어 있습니다. Visual Studio 2005에서는 배포용으로 미리 컴파일할 수 있지만 기본적으로 웹 사이트 프로젝트에 직접 빌드 전 작업이나 빌드 후 작업을 추가할 수는 없습니다. 따라서 Visual Studio 2005에서 빌드에 병합 명령을 추가하는 것은 쉽지 않습니다.

이러한 요구를 해결하기 위해 다운로드하여 별도로 설치할 수 있는 새로운 웹 배포 프로젝트 기능을 사용할 수 있습니다. Visual Studio 2005 웹 배포 프로젝트 설치에는 aspnet_merge.exe 도구도 포함되어 있습니다. 웹 배포 프로젝트가 있으면 명령줄 도구를 실행하거나 빌드 관련 파일을 수동으로 편집해야만 사용할 수 있는 기능을 Visual Studio 2005 내에서 사용할 수 있습니다.

개발 팀은 빌드 시 다음 방법을 사용할 수 있습니다.

  • 사용자 지정 빌드 작업에서 aspnet_compiler.exe와 aspnet_merge.exe를 순서대로 사용. 일반적으로 엔터프라이즈 개발 팀에서는 자체 빌드 서버 시나리오에 맞는 사용자 지정 빌드 스크립트를 작성합니다.
  • 수동으로 만든 MSBuild 파일에 aspnet_compiler.exe 및 aspnet_mer叿䉍/᠀젇ࠁࠂ詀.㷞ɀĀ＀?ÿ氀䀧洀작업을 수동으로 추가한 후 빌드 서버에서 실행할 수 있습니다.
  • Visual Studio 2005에서 웹 사이트와 연결된 웹 배포 프로젝트를 통해 aspnet_compiler.exe 및 aspnet_merge.exe 도구 사용. 이 방법을 사용하면 많은 기능에 UI가 제공되며 MSBuild를 통해 이 작업을 실행할 수도 있습니다.

Visual Studio 2005 웹 배포 프로젝트

웹 배포 프로젝트를 사용하여 빌드용 어셈블리를 제어하고 배포 가능한 웹 사이트를 만드는 데 필요한 추가 작업을 관리할 수 있습니다. 웹 프로젝트가 포함된 솔루션에 새 웹 배포 프로젝트를 추가하여 해당 웹 배포 프로젝트를 사용자의 웹 사이트에 연결할 수 있습니다. 그림 30에서는 웹 사이트 C:\work\psw와 연결된 웹 배포 프로젝트 psw_deploy를 보여 줍니다.

사용자 삽입 이미지

그림 30. 솔루션 탐색기의 Visual Studio 2005 웹 배포 프로젝트 노드

Visual Studio 2005를 열고 웹 사이트를 만든 후에는 솔루션 탐색기에서 웹 사이트 노드를 마우스 오른쪽 단추로 클릭하고Add Web Deployment Project(웹 배포 프로젝트 추가)를 선택하여 웹 배포 프로젝트를 솔루션에 추가할 수 있습니다. 솔루션 탐색기에서 웹 배포 프로젝트를 마우스 오른쪽 단추로 클릭한 다음 여러 배포 설정을 구성할 수 있는 속성 페이지를 표시할 수 있습니다. Visual Studio 2005 웹 배포 프로젝트를 사용하면 다음 작업을 수행할 수 있습니다.

  • aspnet_compiler.exe 옵션을 사용하여 디버그, 릴리스 또는 준비 빌드 등의 여러 빌드 작업을 구성합니다.
  • 해당 구성에서 병합 작업을 구성합니다.
  • 버전 관리 및 서명을 처리합니다.
  • 병합 중에 어셈블리 이름을 조작합니다.
  • Web.config 파일을 수정하는 등의 추가 작업을 배포 프로세스의 일부로 정의합니다.
  • 다양한 사용자 지정 시나리오에 적용할 수 있게 프로젝트 파일을 직접 수정합니다.
  • 개발 타임에 프로젝트의 빌드를 비활성화하거나 배포 대상을 만들 때 웹 사이트 자체의 빌드를 비활성화합니다.

또한 프로젝트 파일을 수동으로 편집하고 사용자 지정 작업 또는 동작을 빌드 전 작업 및 빌드 후 작업으로 추가할 수 있습니다. 자세한 내용은 MSDN 웹 사이트의Using Web Deployment Projects with Visual Studio 2005 (영문)기사를 참조하십시오.

그림 31에서는 출력 어셈블리에 대한 옵션을 설정하기 위한 속성 페이지를 보여 줍니다.

사용자 삽입 이미지

그림 31. Visual Studio 2005 웹 배포 프로젝트용 출력 어셈블리 속성 페이지

이 페이지에서 aspnet_merge.exe 도구에 사용할 수 있는 몇 가지 옵션을 확인할 수 있습니다.

Aspnet_merge.exe 도움말

이 기사에서는 다양한 aspnet_merge.exe 옵션을 설명합니다.-?옵션을 지정하여 aspnet_merge.exe를 실행하면 이러한 옵션은 물론 여기에 나와 있는 다른 옵션에 대한 추가 도움말을 볼 수 있습니다.

요약

Aspnet_merge.exe는 웹 사이트 프로덕션 환경을 관리하는 데 사용할 수 있는 새로운 유틸리티입니다. 이 병합 도구는 웹 사이트를 미리 컴파일할 때 ASP.NET 컴파일러 aspnet_compiler.exe에 의해 생성되는 많은 수의 어셈블리를 결합합니다. 이를 통해 ASP.NET 컴파일러에 의해 생성된 출력을 사용하는 게시 프로세스나 릴리스 관리 프로세스를 보다 쉽게 관리할 수 있습니다.

이 기사에서는 병합 도구와 함께 사용할 수 있는 다양한 옵션과 웹 사이트 미리 컴파일 시 이 병합 도구를 사용하는 방법에 대해 설명합니다. 이 기사의 예제에서는 사용자 지정 빌드 모델에 통합할 수 있는 명령을 보여 줍니다.

Aspnet_merge.exe는 새로운 Visual Studio 2005용 웹 배포 프로젝트 추가 기능과 함께 설치됩니다. 이 추가 기능은 aspnet_compiler.exe 및 aspnet_merge.exe 도구 옵션을 관리하기 위한 포괄적인 UI를 제공하며 Visual Studio 내에서 배포 가능한 웹 사이트를 만드는 작업을 관리할 수 있도록 합니다. 웹 배포 프로젝트에는 웹 사이트 배포 관리에 필요한 빌드 전 단계와 빌드 후 단계를 추가하기 위한 기능도 포함되어 있습니다.

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari
2006/04/20 21:49

출처 -MSDN

 

ADO.NET 2.0의 쿼리 알림

 

Bob Beauchemin
DevelopMentor

2005년 4월
업데이트한 날짜: 2005년 6월

적용 대상:
   ADO.NET 2.0

요약:ADO.NET 2.0 및 SQL Server 2005에 제공되는 새로운 알림 기술을 사용하여 데이터 변경을 다루는 방법을 배워 보십시오.

목차

소개
캐시 솔루션을 제공하는 SqlDependency
SQL Server 2005의 쿼리 알림
최종 사용자 또는 캐시로 알림 발송
데이터베이스 클라이언트에서 쿼리 알림 사용
SqlDependency 사용
SqlNotificationRequest 사용
ASP.NET에서 SqlCacheDependency 사용
향상된 알림 기능
알림을 사용하지 않는 경우: 교훈
결론

소개

모든 실제 관계형 데이터베이스 응용 프로그램에는 으레 많은 조회 테이블이 들어 있습니다. 그래픽 사용자 인터페이스를 코딩하는 경우 이러한 조회 테이블은 드롭다운 목록 상자를 채우는 목록으로 나타납니다. 필자는 조회 테이블을 읽기 전용 테이블과 읽기 위주(read-mostly) 테이블의 두 가지 유형으로 분류합니다. 이들 간의 차이는 테이블의 변경을 유발하는 부분에 있습니다. 직원 회의나 사용자 회의를 통해 테이블을 변경하는 경우는 읽기 전용 테이블로 볼 수 있으며, 회사의 제품 범주가 포함된 테이블은 이에 대한 좋은 예입니다. 이러한 테이블은 회사에서 신제품을 출시하거나 회사가 재편되지 않는 한 변경되지 않습니다. 읽기 위주 테이블은 비교적 일관되게 유지되지만 최종 사용자가 변경할 수 있는 목록입니다. 이러한 테이블은 대개 드롭다운 목록이 아닌 콤보 상자에 나타납니다. 읽기 위주 테이블의 예로는 호칭(term-of-greeting) 테이블을 들 수 있습니다. 응용 프로그램 디자이너는 대개 Ms., Mr., Mrs. 및 Dr. 같은 일반적인 호칭을 생각할 수 있지만 한 번도 생각해 본 적 없는 호칭을 추가하려는 사용자도 늘 있게 마련입니다. 이러한 일이 얼마나 흔한 경우인지를 보여 주는 예로, 필자가 최근 작업한 중간 크기 제품에는 제3 정규형 관계형 데이터베이스에 350-400개의 테이블이 들어 있었습니다. 이 중 250개 정도가 읽기 전용 또는 읽기 위주 테이블인 것으로 추정됩니다.

3계층 응용 프로그램의 대표적인 예라 할 수 있는 일반적인 웹 응용 프로그램에서는 이러한 유형의 테이블을 가능한 한 많이 캐시하게 됩니다. 이렇게 하면 데이터베이스로의 왕복 횟수는 물론 데이터베이스에서의 쿼리 로드를 줄여 신규 주문 같은 사용 사례에서 응답 성능을 높일 수 있습니다. 읽기 전용 테이블의 경우에는 항상 테이블을 캐시에 유지하고 데이터베이스 관리자(DBA)가 테이블을 다시 로드해야 할 경우에만 이따금 캐시를 다시 로드할 수 있도록 하므로 캐시 작업이 간단합니다. 조직에서 기본 데이터베이스 구조와 콘텐츠를 변경하는 회의를 하는 경우는 아마 거의 없을 것입니다. 중간 계층 캐시에서 읽기 위주 조회 테이블을 새로 고치면 약간의 문제가 발생할 수 있습니다. 일정에 따라 캐시를 간헐적으로 새로 고쳐도 원하는 동작이 만들어지지 않을 뿐 아니라, 사용자는 다른 사용자의 변경 내용을 바로 확인하지 못합니다. 지원 담당자가 다른 응용 프로그램을 사용하여 새 항목을 추가하고 이를 사용하려는 친구에게 인스턴트 메신저 메시지를 보낼 수 있겠지만, 선택한 친구 목록은 새 항목에 포함되지 않습니다. 게다가 다른 사용자가 "누락된 목록 항목"을 다시 추가하려고 하면 항목이 이미 존재한다는 내용의 데이터베이스 오류 메시지가 표시됩니다. 읽기 위주 테이블에 "업데이트 지점"이 둘 이상인 경우에는 대개 이러한 문제로 인해 해당 테이블의 캐시 작업이 이루어지지 않습니다.

예전의 프로그래머들은 메시지 대기열을 사용하는 수작업 솔루션, 파일에 쓰는 트리거 또는 응용 프로그램 외부의 누군가가 읽기 위주 테이블을 업데이트하면 이를 캐시에 알리는 대역 외 프로토콜에 의존했습니다. 이러한 "알림(signaling)" 솔루션은 단순히 행이 추가 또는 변경되었으므로 캐시를 새로 고쳐야 한다는 내용을 캐시에 알리는 역할만 합니다. 특정 행이 변경 또는 추가되었음을 캐시에 알리는 것은 조금 다른 문제이며, 분산 데이터베이스 및 트랜잭션 또는 병합 복제 영역에 해당됩니다. 오버헤드가 낮은 알림 솔루션에서는 프로그램에 "잘못된 캐시"라는 메시지가 표시되면 전체 캐시를 새로 고치기만 할 뿐입니다.

캐시 솔루션을 제공하는 SqlDependency

SQL Server 2005 및 ADO.NET 2.0 사용자는 이제 SqlClient 데이터 공급자와 쿼리 알림이라는 데이터베이스에 기본 제공되는 알림 솔루션을 사용할 수 있습니다. 이는 이러한 일상적인 문제를 해결하는 사용이 편리한 최초의 기본 제공 솔루션입니다. 쿼리 알림은 ASP.NET 2.0의 기본 기능에서도 직접 지원됩니다. ASP.NET 캐시는 알림에 등록할 수 있으며 이 알림은 AST.NET에서 사용되는 페이지 및 페이지 조각 캐시와도 함께 사용할 수 있습니다.

이처럼 유용한 기능을 수행하는 인프라는 SQL Server 2005 쿼리 엔진, SQL Server Service Broker, 시스템 저장 프로시저인sp_DispatcherProc, ADO.NET SqlNotification(System.Data.Sql.SqlNotificationRequest),SqlDependency(System.Data.SqlClient.SqlDependency) 클래스 및 ASP.NET Cache(System.Web.Caching.Cache) 클래스로 구성되어 있습니다. 요컨대 이 인프라는 다음과 같이 작동합니다.

  1. 각 ADO.NETSqlCommand에는 알림 요청을 나타내는Notification속성이 들어 있습니다. SqlCommand가 실행될 때 Notification 속성이 있으면 알림 요청을 나타내는 네트워크 프로토콜(TDS)이 요청에 추가됩니다.
  2. SQL Server는 쿼리 알림 인프라를 사용하여 요청된 알림에 대해 구독을 등록하고 명령을 실행합니다.
  3. SQL Server는 SQL DML 문에서 처음에 반환된 행 집합을 변경시킬 수 있는 부분이 있는지 "감시"합니다. 변경이 발생하면 Service Broker 서비스로 메시지가 전송됩니다.
  4. 이 메시지는 다음과 같은 작업을 수행합니다.
    1. 등록된 클라이언트에 다시 알림이 발생하도록 합니다.
    2. 고급 클라이언트가 사용자 지정 처리를 할 수 있도록 Service Broker 서비스 대기열에 그대로 유지됩니다.

사용자 삽입 이미지

그림 1. 쿼리 알림의 상위 개요

ASP.NETSqlCacheDependency(System.Web.Caching.SqlCacheDependency) 클래스 및OutputCache지시문은SqlDependency를 통해 자동 알림 기능을 사용합니다. 더 많은 컨트롤이 필요한 ADO.NET 클라이언트는SqlNotificationRequest를 사용하고 Service Broker 대기열을 수동으로 처리하여 필요한 사용자 지정 구문을 모두 구현할 수 있습니다. Service Broker에 대한 자세한 설명은 이 기사에서는 다루지 않지만 샘플 서적의 A First Look at SQL Server 2005 for Developers(영문) 부분과 Roger Wolter가 저술한 "A First Look at SQL Server 2005 Service Broker"(영문) 기사를 참조하면 이를 이해하는 데 많은 도움이 될 것입니다.

계속하기 전에, 행 집합이 변경되면SqlNotificationRequest또는SqlDependency에 각각 알림 메시지가 하나씩 전달되는 부분을 확실히 이해하고 넘어가야 합니다. 이 메시지는 변경을 유발하는 부분이 데이터베이스 INSERT 문이든, 하나 이상의 행을 삭제하는 DELETE 문이든, 하나 이상의 행을 업데이트하는 UPDATE 문이든 관계없이 동일합니다. 알림에 변경된 특정 행 또는 변경된 수와 관련된 정보는 들어 있지 않습니다. 캐시 개체나 사용자의 응용 프로그램에서 이러한 단일 변경 메시지를 받으면 전체 행 집합을 새로 고치고 알림에 다시 등록하는 방법 밖에는 없습니다. 여러 개의 메시지를 수신하지는 않으며 하나의 메시지가 발생한 후에는 데이터베이스의 사용자 구독이 종료됩니다. 또한 쿼리 알림 프레임워크는 이벤트에 대한 알림은 많이 받을수록 좋다는 것을 전제로 하여 작동합니다. 알림은 행 집합이 변경될 때는 물론 행 집합에 참여한 테이블이 삭제 또는 변경될 때, 데이터베이스를 재활용할 때 등과 같은 경우에도 전송됩니다. 캐시 또는 프로그램의 응답은 캐시된 데이터를 새로 고치고 알림에 다시 등록하는 것과 관계없이 대개 동일합니다.

지금까지 관련된 일반적인 구문에 대해 알아보았으므로 이제는 다음과 같은 세 가지 관점에서 작동 원리를 자세히 살펴보겠습니다.

  1. SQL Server의 쿼리 알림 구현 방식 및 옵션 발송자의 작동 원리
  2. SqlClientSqlDependencySqlNotificationRequest가 클라이언트/중간 계층에서 작동하는 방식
  3. ASP.NET 2.0의SqlDependency지원 방식

SQL Server 2005의 쿼리 알림

SQL Server는 서버 수준에서 클라이언트의 쿼리를 일괄적으로 처리합니다. 각 쿼리(여기서 쿼리는SqlCommand.CommandText속성임)에는 일괄 처리가 하나만 포함될 수 있지만, 일괄 처리에는 여러 개의 T-SQL 문이 들어 있을 수 있습니다. SqlCommand는 여러 개의 T-SQL 문이 포함될 수 있는 저장 프로시저나 사용자 정의 함수를 실행하는 데도 사용할 수 있습니다. 또한 SQL Server 2005에서는 클라이언트의 쿼리에 알림을 배달하는 Service Broker 서비스 이름, 알림 ID(문자열) 및 알림 시간 제한과 같은 세 가지 추가 정보가 포함될 수 있습니다. 쿼리 요청에 이러한 세 가지 정보가 나타나 있고 요청에 SELECT 또는 EXECUTE 문이 들어 있으면 SQL Server는 해당 쿼리에 의해 만들어진 모든 행 집합에 다른 SQL Server 세션의 변경 내용이 있는지 "감시"하게 됩니다. 저장 프로시저를 실행할 때처럼 여러 개의 행 집합이 생성되는 경우 SQL Server는 모든 행 집합을 "감시"합니다.

그렇다면 여기서 행 집합을 "감시"한다는 것은 어떤 의미이며 SQL Server는 이를 어떤 식으로 수행할까요? 행 집합의 변경 내용을 탐지하는 일은 SQL Server 엔진에서 수행하는 작업의 일부이며, 인덱싱된 뷰의 동기화를 위한 SQL Server 2000의 변경 탐지 기능부터 적용된 메커니즘이 사용됩니다. Microsoft는 SQL Server 2000부터 인덱싱된 뷰 개념을 도입했습니다. SQL Server의 뷰는 하나 이상의 테이블에 있는 열에 대한 쿼리로 구성되어 있습니다. 뷰 이름은 테이블 이름처럼 사용할 수 있습니다. 예를 들면 다음과 같습니다.

CREATE VIEW WestCoastAuthorsASSELECT * FROM authors WHERE state IN ('CA', 'WA', 'OR')

이제 뷰를 다음과 같이 쿼리의 테이블처럼 사용할 수 있습니다.

SELECT au_id, au_lname FROM WestCoastAuthors WHERE au_lname LIKE 'S%'

대부분의 프로그래머에게 뷰는 익숙하지만 인덱싱된 뷰는 생소할 수 있습니다. 인덱싱되지 않은 뷰의 뷰 데이터는 데이터베이스에 개별 복사본으로 저장되지 않으므로 뷰를 사용할 때마다 기본 제공 쿼리가 실행됩니다. 따라서 위의 예에서는 행 집합 WestCoastAuthors를 가져오는 쿼리가 실행되며 이 쿼리에는 필요한 특정 WestCoastAuthors를 끌어오는 술어가 포함됩니다. 인덱싱된 뷰는 데이터의 복사본을 저장하므로 WestCoastAuthors를 인덱싱된 뷰로 만들면 이들 작성자의 데이터 복사본을 두 개씩 얻게 됩니다. 이제 인덱싱된 뷰 또는 원본 테이블 중 하나를 경로로 선택하여 데이터를 업데이트할 수 있습니다. 그에 따라 SQL Server는 두 물리적 데이터 저장소에서 모두 변경 내용을 탐지한 후 다른 데이터 저장소에 이를 적용해야 합니다. 이러한 변경 탐지 메커니즘은 쿼리 알림이 설정되어 있을 때 엔진에서 사용하는 메커니즘과 동일합니다.

변경 탐지 특유의 구현 방식으로 인해 모든 뷰를 인덱싱할 수는 없습니다. 인덱싱된 뷰에 적용되는 이러한 제한 사항은 쿼리 알림에 사용되는 쿼리에도 적용됩니다. 예를 들어 WestCoastAuthors 뷰의 경우 뷰가 작성된 방식으로 인덱싱할 수는 없습니다. 이 뷰를 인덱싱하려면 뷰 정의에 두 부분으로 구성된 이름을 사용하고 모든 행 집합 열 이름을 명시적으로 지정해야 합니다. 그러면 이제 뷰를 변경하여 인덱싱을 수행해 보겠습니다.

CREATE VIEW WestCoastAuthorsWITH SCHEMABINDINGASSELECT au_id, au_lname, au_fname, address, city, state, zip, phone FROM dbo.authors WHERE state in ('CA', 'WA', 'OR')

여기서 인덱싱된 뷰 규칙을 따르는 쿼리만 알림과 함께 사용할 수 있습니다. 쿼리 결과의 변경 여부를 확인하는 데도 동일한 메커니즘이 사용되지만 인덱싱된 뷰에서처럼 쿼리 알림으로 인해 SQL Server에 데이터의 복사본이 생성되지는 않습니다. 인덱싱된 뷰에 대한 규칙 목록은 상당히 방대하며 SQL Server 2005 온라인 설명서에서 확인할 수 있습니다. 쿼리가 알림 요청과 함께 전송되고 규칙을 따르지 않는 경우 SQL Server는 즉시 "유효하지 않은 쿼리입니다"라는 알림을 이유와 함께 게시합니다. 그렇다면 알림은 "어디에 게시"될까요?

SQL Server 2005에서는 Service Broker 기능을 사용하여 알림을 게시합니다. Service Broker는 SQL Server에 기본 제공되는 비동기 대기열 기능이며, 쿼리 알림은 이 Service Broker 서비스를 사용합니다. 이 경우 서비스는 비동기 메시지의 대상이며 메시지는 계약이라는 일련의 특정 규칙을 따라야 합니다. Service Broker 서비스는 항상 물리적 메시지 대상인 대기열과 관련되어 있습니다. 쿼리 알림에 대한 계약은 SQL Server에 기본적으로 제공되며 이름은 http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification입니다.

참고   SQL Server의 계약 개체 이름은 URL이지만 위치를 나타내지 않습니다. 다시 말해 테이블 이름인 dbo.authors처럼 개체 이름에 불과합니다.

여기까지 종합해 보면 쿼리 알림 메시지의 대상은 적절한 계약을 지원하는 서비스라는 점을 알 수 있습니다. 서비스 등을 정의하는 SQL DDL은 다음과 같습니다.

CREATE QUEUE mynotificationqueueCREATE SERVICE myservice ON QUEUE mynotificationqueue ([http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification])GO

이제 쿼리 알림 요청에서 myservice 서비스를 대상으로 사용할 수 있을 것입니다. SQL Server는 서비스에 메시지를 보내는 방식으로 알림을 전송합니다. 고유한 서비스를 사용하거나 SQL Server에서 MSDB 데이터베이스의 기본 제공 서비스를 사용하도록 할 수도 있습니다. 고유한 서비스를 사용하는 경우에는 메시지를 읽고 처리하는 코드를 작성해야 합니다. 하지만 MSDB에 기본 제공되는 서비스를 사용하는 경우에는 메시지를 배달하는 미리 작성된 코드가 제공됩니다. 이 부분에 대해서는 나중에 다시 설명하겠습니다.

쿼리 알림에서 Service Broker를 사용하기 때문에 몇 가지 추가 요구 사항이 적용됩니다.

  1. Service Broker는 쿼리 알림을 실행하는 데이터베이스에서 활성화해야 합니다. 베타 2의 경우 AdventureWorks 샘플 데이터베이스에는 Service Broker가 기본적으로 활성화되어 있지 않지만, "ALTER DATABASE SET ENABLE_BROKER" DDL 문을 사용하여 활성화할 수 있습니다.
  2. 쿼리를 전송하는 사용자는 쿼리 알림을 구독할 수 있는 권한을 가지고 있어야 합니다. 쿼리 알림 구독은 데이터베이스마다 수행합니다. 다음 DDL을 실행하면 사용자에게 현재 데이터베이스에서 구독을 수행할 수 있는 'bob' 권한이 부여됩니다. GRANT SUBSCRIBE QUERY NOTIFICATIONS TO bob

최종 사용자 또는 캐시로 알림 발송

지금까지 알림 요청과 함께 올바른 쿼리 일괄 처리를 SQL Server로 전송했습니다. SQL Server는 행 집합을 감시하며 누군가 행 집합을 변경하면 선택한 서비스로 메시지가 전송됩니다. 그렇다면 이제 무엇을 해야 할까요? 먼저, 메시지를 읽고 알림이 발생할 때 필요한 모든 로직을 수행하는 사용자 지정 코드를 작성합니다. 아니면 기본 제공되는 발송자에서 이를 자동으로 수행하도록 할 수도 있습니다. 그러면 발송자에 대해 한번 살펴보겠습니다.

사용자 지정 서비스를 지정하지 않으면 쿼리 알림은 http://schemas.microsoft.com/SQL/Notifications/QueryNotificationService라는 MSDB 데이터베이스의 기본 서비스를 사용합니다.서비스 대기열에 메시지가 도착하면 sp_DispatcherProc이라는 대기열과 관련된 저장 프로시저에서 메시지를 자동으로 처리합니다. 한 가지 흥미로운 점은 이 프로시저는 .NET으로 작성된 코드를 사용한다는 점으로, 쿼리 알림을 자동으로 배달하려면 SQL Server 2005 인스턴스에 .NET CLR(공용 언어 런타임)을 로드하는 기능이 활성화되어 있어야 합니다. .NET CLR의 로드는 SQL Server 인스턴스별로 활성화하거나 비활성화할 수 있습니다.

쿼리 알림 메시지가 도착하면sp_DispatcherProc(이하 "발송자")은SqlDependency알림 대기열의 쿼리 알림 구독 목록을 검색하여 각 구독자에게 메시지를 보냅니다. 서버는 발송자를 사용하여 데이터가 변경된 사실을 클라이언트에 알립니다. 이렇게 되면 클라이언트가 알림에 대해 폴링할 필요가 없을 뿐 아니라 알림을 수신하기 위해 SQL Server에 대한 연결을 열어 놓을 필요가 없기 때문에 매우 유용합니다. 발송자는 HTTP 프로토콜/TCP 및 개인 프로토콜을 사용하여 이 알림을 각 구독자에게 보냅니다. 서버와 클라이언트 간 통신은 선택적으로 인증할 수 있습니다. 알림이 배달되고 나면 활성 구독 목록에서 구독이 삭제됩니다. 클라이언트 구독별로 알림을 하나씩만 받으므로 쿼리를 다시 전송하고 다시 구독하는 일은 클라이언트에서 담당합니다.

데이터베이스 클라이언트에서 쿼리 알림 사용

지금까지 내부 작업을 모두 살펴보았으니 이제 이를 사용하는 ADO.NET 클라이언트를 작성해 보겠습니다. 어째서 비교적 간단한 클라이언트측 코드를 작성하면서 이처럼 많은 설명이 필요했을까요? 코드는 상당히 쉽게 작성할 수 있지만 반드시 규칙을 따라야만 합니다. 가장 일반적으로 발생하는 문제는 알림에 대해 유효하지 않은 쿼리를 전송한 다음 Service Broker와 사용자 권한을 설정하는 과정을 잊어버리는 경우입니다. 이로 인해 이처럼 강력한 기능이 애물단지로 전락하게 되었을 뿐 아니라, 일부 베타 테스터는 기능이 제대로 작동하지 않는다는 생각까지 하게 되었습니다. 따라서 간단한 예비 작업과 조사를 수행하면 많은 도움이 됩니다. 마지막으로 발송자에 대해 Service Broker 서비스 같은 속성과 프로토콜을 지정하게 되므로 먼저 내부 작업을 수행하는 편이 유익합니다. 이제는 이러한 용어들이 어떤 의미인지 알게 되었을 것입니다.

ADO.NET에서 쿼리 알림 클라이언트를 작성할 수 있는 것처럼 여기서는 OLE DB나 새로운 HTTP 웹 서비스 클라이언트를 사용하여 이를 작성할 예정입니다. 하지만 유의해야 할 부분은 쿼리 알림은 클라이언트측 코드를 통해서만 사용할 수 있는 점입니다. 이 기능을 T-SQL과 바로 함께 사용하거나 SqlServer 데이터 공급자를 사용하여 SQL Server와 통신하는 SQLCLR 프로시저 코드와 함께 사용할 수는 없습니다.

System.Data.dll어셈블리에는SqlDependencySqlNotificationRequest라는 두 개의 클래스가 들어 있습니다.SqlDependency는 발송자를 사용하여 자동 알림을 실행하려는 경우 사용합니다.SqlNotificationRequest는 알림 메시지를 직접 처리하려는 경우에 사용합니다. 그럼, 지금부터 이들 클래스의 예제를 살펴보겠습니다.

SqlDependency 사용

SqlDependency의 사용 단계는 간단합니다. 먼저, 쿼리 알림을 수행해야 하는 SQL 문이 들어 있는SqlCommand를 만들고,SqlCommandSqlDependency를 연결합니다. 그런 다음SqlDependencyOnChanged이벤트에 대해 이벤트 처리기를 등록합니다. 그런 다음SqlCommand를 실행합니다. DataReader를 처리하고 닫는 것은 물론 연관된SqlConnection까지 닫을 수 있습니다. 이제 행 집합이 변경되면 발송자를 통해 알림을 수신하게 됩니다. 이 개체에 대한 이벤트는 서로 다른 스레드에서 발생합니다. 따라서 코드 실행 중에 이벤트가 발생하는 상황을 처리할 수 있도록 대비해야 합니다. 경우에 따라 이벤트가 발생하는 상황에 일괄 처리 결과를 계속 처리할 수도 있습니다. 코드는 다음과 같습니다.

using System;using System.Data;using System.Data.SqlClient;static void Main(string[] args){ string connstring = GetConnectionStringFromConfig(); using (SqlConnection conn = new SqlConnection(connstring)) using (SqlCommand cmd = // 2-part table names, no "SELECT * FROM ..." new SqlCommand("SELECT au_id, au_lname FROM dbo.authors", conn)) { try { // cmd와 연관된 종속성을 만듭니다. SqlDependency depend = new SqlDependency(cmd); // 처리기를 등록합니다. depend.OnChanged += new OnChangedEventHandler(MyOnChanged); conn.Open(); SqlDataReader rdr = cmd.ExecuteReader(); // DataReader를 처리합니다. while (rdr.Read()) Console.WriteLine(rdr[0]); rdr.Close(); // 무효화가 완료될 때까지 기다립니다. Console.WriteLine("Press Enter to continue"); Console.ReadLine(); } catch (Exception e) { Console.WriteLine(e.Message); } }}static void MyOnChanged(object caller, SqlNotificationEventArgs e){ Console.WriteLine("result has changed"); Console.WriteLine("Source " + e.Source); Console.WriteLine("Type " + e.Type); Console.WriteLine("Info " + e.Info);(참고: 프로그래머 코멘트는 샘플 프로그램
파일에는 영문으로 제공되며 기사에는 설명을 위해 번역문으로 제공됩니다.)}

Visual Basic .NET에서 친숙한WithEvents키워드와SqlDependency를 함께 사용해도 이와 동일한 코드를 작성할 수 있습니다. 이 프로그램은 기본 결과의 변경 횟수에 관계없이OnChanged이벤트를 한 번만 가져와 처리합니다. 실제 사용 시 알림을 받았을 때 해야 할 일은 새로운 알림과 함께 명령을 다시 전송하고 그 결과를 바탕으로 캐시를 새 데이터로 새로 고치는 것입니다. 위 예제의Main()에 여기서 작성한 코드를 가져와 명명된 루틴으로 이동하면 코드의 형태는 다음과 같습니다.

static void Main(string[] args){ GetAndProcessData(); UpdateCache(); // 사용자가 프로그램을 종료할 때까지 기다립니다. Console.WriteLine("Press Enter to continue"); Console.ReadLine();}static void MyOnChanged(object caller, SqlNotificationEventArgs e){ GetAndProcessData(); UpdateCache();}

일부 단락을 보면 이것이 ASP.NET Cache 클래스를 데이터 캐시로 사용하여 ASP.NET 2.0를 실행하는 것과 완전히 똑같다는 사실을 알 수 있습니다.

SqlDependency를 사용하는 경우에는 SQL Server 2005의 발송자 구성 요소를 통해 클라이언트에 연결하고 알림 메시지를 보냅니다. 이는 SqlConnection을 사용하지 않는 대역 외 통신입니다. 이는 또한 클라이언트가 SQL Server를 통해 "네트워크를 사용할 수 있어야"하며 방화벽 및 네트워크 주소 변환으로 인해 문제가 발생할 수 있다는 것을 의미합니다. 향후 베타 버전이 출시되면 포트 구성에 대한 제어 성능이 강화되어 방화벽을 보다 자유롭게 사용할 수 있을 것입니다.SqlDependency생성자에 매개 변수를 지정하여 서버와 클라이언트가 통신하는 방식을 완벽하게 구성할 수 있습니다. 다음은 이에 대한 예제입니다.

SqlDependency depend = new SqlDependency(cmd, null, SqlNotificationAuthType.None, SqlNotificationEncryptionType.None, SqlNotificationTransports.Tcp, 10000);

SqlDependency생성자를 사용하면 기본값 대신 다양한 동작을 선택할 수 있습니다. 변경할 동작 중 가장 유용한 것은 서버에서 클라이언트와 연결하는 데 사용하는 유선 전송입니다. 이 예제에서는SqlNotificationTransports.Tcp를 사용하므로 서버에서는 TCP 또는 HTTP를 사용할 수 있습니다. 이 매개 변수의 기본값은SqlNotificationTransports.Any이며, 서버에서는 이를 통해 사용할 전송을 "결정"할 수 있습니다.Any가 지정된 경우 서버는 클라이언트 운영 체제에 커널 모드 HTTP 지원이 포함된 경우에는 HTTP를 선택하고, 그렇지 않은 경우에는 TCP를 선택하게 됩니다. Windows Server 2003 및 Windows XP(SP2)에는 커널 모드 HTTP 지원이 포함되어 있습니다. 또한 네트워크를 통해 메시지를 보내기 때문에 사용할 인증 형식을 지정할 수 있습니다.EncryptionType은 현재 매개 변수로 사용되고 있지만 이후 베타 버전에서는 제거될 예정입니다. 현재 두 값 모두에 대한 기본값은None입니다. SqlNotificationAuthType은 통합 인증도 지원합니다. 또한 구독에 대한 시간 제한 값과 SQL Server Service Broker 서비스의 이름을 명시적으로 지정할 수 있습니다. 서비스 이름은 예제에서처럼 대개 null로 설정되지만 기본 제공 서비스인SqlQueryNotificationService를 명시적으로 지정할 수도 있습니다. 대개 다시 정의할 확률이 가장 높은 매개 변수는SqlNotificationTransport와 시간 제한입니다. 이러한 매개 변수는 서버측 발송자의 동작을 지정하므로SqlDependency에만 적용할 수 있습니다.SqlNotificationRequest를 사용하는 경우에는 발송자를 사용하지 않습니다.

SqlNotificationRequest 사용

SqlNotificationRequest를 사용하려면SqlDependency보다 약간 복잡한 설정 과정을 거쳐야 하지만 메시지 처리는 프로그램에서 담당하게 됩니다.SqlDependency를 사용하면 서버의 알림이 MSDB의SqlQueryNotificationService로 전송되어 메시지가 자동으로 처리됩니다. 하지만SqlNotificationRequest를 사용하는 경우에는 메시지를 직접 처리해야 합니다. 다음은SqlNotificationRequest와 이 기사의 앞부분에서 정의한 서비스를 사용하는 간단한 예제입니다.

using System;using System.Data;using System.Data.Sql;using System.Data.SqlClient;class Class1{ string connstring = null; SqlConnection conn = null; SqlDataReader rdr = null; static void Main(string[] args) { connstring = GetConnectionStringFromConfig(); conn = new SqlConnection(connstring)); Class1 c = new Class1(); c.DoWork(); } void DoWork() { conn.Open(); rdr = GetJobs(2); if (rdr != null) { rdr.Close(); WaitForChanges(); } conn.Dispose(); } public SqlDataReader GetJobs(int JobId) { using (SqlCommand cmd = new SqlCommand( "Select job_id, job_desc from dbo. jobs where job_id = @id", conn)) { try { cmd.Parameters.AddWithValue("@id", JobId); SqlNotificationRequest not = new SqlNotificationRequest(); not.Id = new Guid(); // 이것은 notificationqueue라는 대기열과 연관된 pubs 데이터베이스의 // MyService라는 서비스여야 합니다(아래 참조). // 서비스는 QueryNotifications 계약에 따라야 합니다. not.Service = "myservice"; not.Timeout = 0; // 알림 요청을 연결합니다. cmd.Notification = not; rdr = cmd.ExecuteReader(); while (rdr.Read()) Console.WriteLine(rdr[0]); rdr.Close(); } catch (Exception ex) { Console.WriteLine(ex.Message); } return rdr; } } public void WaitForChanges() { // 알림이 대기열에 나타날 때까지 // 기다렸다가 직접 읽고 확인합니다. using (SqlCommand cmd = new SqlCommand( "WAITFOR (Receive convert(xml,message_body) from notificationqueue)", conn)) { object o = cmd.ExecuteScalar(); // 필요에 따라 알림 메시지를 처리합니다. Console.WriteLine(o); } }

SqlNotificationRequest를 사용하는 장점(추가 작업이기도 함)은 알림을 기다렸다가 직접 처리해야 한다는 점입니다.SqlDependency를 사용하는 경우에는 알림을 수신하기 전까지 데이터베이스에 다시 연결할 필요가 없습니다.SqlNotificationRequest알림은 기다릴 필요가 없으며 대기열만 가끔씩 폴링하면 됩니다.SqlNotificationRequest의 또 다른 사용 예로는 알림이 발생하면 실행조차 되지 않은 특수한 응용 프로그램을 작성하는 경우입니다. 응용 프로그램이 시작되면 대기열에 연결되어 이전 응용 프로그램 실행의 "영구 캐시" 결과가 더 이상 유효하지 않음을 확인할 수 있습니다.

알림을 몇 시간 또는 몇 일 동안 기다릴 수 있는 응용 프로그램에 대해 설명하다 보면, "데이터가 변경되지 않으면 알림은 언제 사라집니까?"와 같은 질문을 받게 됩니다. 데이터베이스의 구독 테이블에서 삭제되는 것처럼 알림을 사라지게 하는 유일한 요인은 알림이 발생되거나 알림이 만료되는 경우뿐입니다. 알림 구독이 SQL 리소스를 사용하고 쿼리 및 업데이트에 오버헤드를 추가하기 때문에 이를 기다리는 일이 귀찮은 데이터베이스 관리자는 SQL Server에서 알림을 수동으로 삭제할 수도 있습니다. 먼저 SQL Server 2005 동적 뷰에 대해 알림을 쿼리하고 성가신 알림 구독을 검색한 다음 명령을 실행하여 제거하면 됩니다.

-- 모든 구독을 찾습니다. SELECT * FROM sys.dm_qn_subscriptions -- 원하는 구독의 ID를 선택한 다음 -- ID = 42인 구독을 삭제합니다. KILL QUERY NOTIFICATION SUBSCRIPTION 42

ASP.NET에서 SqlCacheDependency 사용

ASP.NET Cache 클래스에도 알림을 연결할 수 있습니다. ASP.NET 2.0에서는 CacheDependency 클래스를 하위 클래스로 지정할 수 있으며,SqlCacheDependencySqlDependency를 캡슐화하고 다른 ASP.NET CacheDependency와 마찬가지로 작동합니다.SqlCacheDependency는 사용하는 버전이 SQL Server 2005이든, 이전 버전의 SQL Server든 관계없이 작동하므로SqlDependency보다 효율성이 뛰어납니다. 물론 SQL Server 2005 이전 버전에서는 완전히 다른 방식으로 구현되어 있습니다.

이전 버전의 SQL Server를 사용하는 경우에는 "감시"할 테이블의 트리거를 사용하는 방식으로SqlCacheDependency가 작동하게 됩니다. 이러한 트리거는 다른 SQL Server 테이블에 행을 쓰는 역할을 하며, 그러면 이 테이블이 폴링됩니다. 어떤 테이블의 종속성 및 폴링 간격 값을 활성화할지도 구성할 수 있습니다. SQL Server 2005 이전 구현에 대한 자세한 설명은 이 기사의 범위를 벗어나므로 자세한 내용은 Improved Caching in ASP.NET 2.0(영문)을 참조하십시오.

SQL Server 2005를 사용하는 경우 위의 ADO.NET 예제와 유사하게SqlCacheDependency에서SqlDependency인스턴스를 캡슐화합니다. 다음은SqlCacheDependency를 사용하는 간단한 코드 예제입니다.

// Page.Load를 호출했습니다.CreateSqlCacheDependency(SqlCommand cmd){ SqlCacheDependency dep = new SqlCacheDepedency(cmd); Response.Cache.SetExpires(DateTime.Now.AddSeconds(60); Response.Cache.SetCacheability(HttpCacheability.Public); Response.Cache.SetValidUntilExpires(true); Response.AddCacheDependency(dep);}

사용이 편리한 뛰어난 기능 한 가지는SqlCacheDependency가 페이지 또는 페이지 조각 캐시에도 연결된다는 점입니다. 또한 특정 ASP.NET OutputCache 지시문에서 모든SqlCommands를 선언적으로 활성화할 수 있습니다. 여기에서는 페이지의 모든SqlCommands에 대해 동일한SqlDependency가 사용되며, SQL Server 2005 데이터베이스에 사용되는 형태와 유사합니다.

<%OutputCache SqlDependency="CommandNotification" ... %>

CommandNotification은 "SQL Server 2005 및SqlDependency를 사용한다"는 의미의 키워드 값입니다. 이전 버전의 SQL Server를 사용하는 경우에는 이 지시문 매개 변수의 구문이 완전히 다릅니다. 또한 특정 운영 체제 버전에서 ASP.NET 2.0을 실행하면CommandNotification키워드 값만 활성화됩니다.

향상된 알림 기능

SQL Server 쿼리 알림의 디자인 정책은 클라이언트에 대해 알림을 빠뜨리는 것보다는 지나칠 정도로 자주 보내는 게 낫다는 것을 골자로 하고 있습니다. 대개 다른 누군가 행을 변경하여 캐시가 무효화되면 알림을 받게 되지만 항상 그런 것은 아닙니다. 예를 들어 DBA가 데이터베이스를 재활용하면 알림을 받게 됩니다. 또한 쿼리의 테이블 중 하나라도 변경되거나 삭제되거나 잘려도 알림을 받습니다. 쿼리 알림은 SQL Server 리소스를 소비하므로 SQL Server 리소스의 스트레스가 심각해지면 내부 테이블에서 쿼리 알림을 제거하기 시작할 수 있으며 이런 경우에도 클라이언트에서 알림을 받게 됩니다. 또한 각 알림 요청에는 시간 제한 값이 들어 있으므로 구독 제한 시간이 초과되면 알림을 수신합니다.

SqlDependency를 사용하는 경우 발송자는 SqlNotificationEventArgs 인스턴스에 이 정보를 요약합니다. 이 클래스에는 Info, Source 및 Type의 세 가지 속성이 포함되어 있으며, 이를 통해 알림을 유발하는 부분을 지정할 수 있습니다.SqlNotificationRequest를 사용하는 경우 대기열 메시지의message_body필드에 이와 동일한 정보가 포함된 XML 문서가 있지만 XPath 또는 XQuery를 사용하여 직접 구문 분석해야 합니다. 다음은 앞의 ADO.NETSqlNotificationRequest예제에서 만든 샘플 XML 문서입니다.

<qn:QueryNotification xmlns:qn="http://schemas.microsoft.com/SQL/Notifications/QueryNotification" id="2" type="change" source="data" info="update" database_id="6" user_id="1"><qn:Message>{CFD53DDB-A633-4490-95A8-8E837D771707}</qn:Message></qn:QueryNotification>

필자는job_id = 5인 행에서job_desc열 값을"new job"으로 변경하여 이 알림을 만들었지만 message_body에는 이 정보가 표시되지 않습니다. 이를 통해 알림 프로세스의 마지막 몇 가지 차이점을 확인할 수 있습니다. 알림의 기능은 SQL 문을 통해 어떤 항목이 변경되어 행 집합이 변경될 수 있음을 인식하는 정도에 그친다는 사실입니다. 알림 기능으로는 UPDATE 문이 행의 실제 값을 변경하지 않는 경우까지는 인식하지 못합니다. 예를 들어, 행을job_desc = "new job"에서job_desc = "new job"으로 변경하면 알림이 생성됩니다. 또한 쿼리 알림은 비동기적으로 수행되며 명령 또는 일괄 처리를 실행할 때 등록되므로 행 집합 읽기를 마치기 전에 알림을 받을 수 있습니다. 규칙을 따르지 않는 쿼리를 전송하는 경우에도 즉시 알림을 받을 수 있습니다. 여기서 규칙은 앞에서 언급한 인덱싱된 뷰에 대한 규칙입니다.

알림을 사용하지 않는 경우: 교훈

이제 쿼리 알림이 작동하는 방식을 이해했으므로 이를 어디에 사용해야 할지 명백해졌습니다. 바로 읽기 위주 조회 테이블입니다. 각 알림 행 집합은 SQL Server의 리소스를 처리하므로 이를 읽기 전용 테이블에 사용하는 것은 여러모로 쓸데없는 낭비입니다. 또한 동시에 "감시"하는 서로 다른 행 집합 수만 지나치게 많아질 뿐이므로 임시 쿼리에도 사용할 필요가 없습니다. 유용한 내부 작업 관련 정보 한 가지는 SQL Server는 서로 다른 매개 변수 집합을 사용하는 매개 변수가 지정된 쿼리에 대해 알림 리소스를 끌어온다는 사실입니다. 위 예제의SqlNotificationRequest에서처럼 매개 변수가 지정된 쿼리를 사용하면 항상 이러한 이점을 얻는 것은 물론 성능을 높일 수도 있습니다. 이 이야기를 듣고 염려할 수도 있겠지만 이처럼 강력한 기능을 사용한다고 해서 적절한 알림을 받지 않는 것은 아닙니다. user1이 A-M에서 au_lname인 제작자를 감시하고 user2가 au_lname 값을 매개 변수로 사용하여 N-Z에서 au_lname을 감시하는 경우 각 사용자는 각 하위 집합에 "해당하는" 알림만 받게 됩니다.

마지막으로 한 가지 주의할 점은 어떤 사람은 알림 응용 프로그램을 떠올릴 때 시장 가격이 변화하고 화면이 끊임없이 바뀌며 주식 거래자들로 가득 찬 공간을 상상한다는 점입니다. 이는 이 기능을 명백히 잘못 사용하는 것이며 그 이유는 다음과 같은 두 가지로 볼 수 있습니다.

  1. 행 집합은 계속해서 변하므로 네트워크는 쿼리 알림과 쿼리 새로 고침 요청으로 넘쳐 날 수 있습니다.
  2. 몇 명 이상의 사용자가 모두 같은 데이터를 "감시"하는 경우 각각의 알림으로 인해 많은 사용자가 동일한 결과에 대해 동시에 다시 쿼리하는 상황이 발생합니다. 이렇게 되면 SQL Server는 동일한 데이터에 대한 지나치게 많은 요청으로 넘치게 될 수 있습니다.

프로그래머가 이 기능을 잘못 사용할 수 있다고 생각되더라도 베타 2 이후의 SQL Server에서는 DBA가 동적 관리 뷰를 통해 이러한 기능을 모니터링할 수 있도록 하는 자세한 정보를 제공하므로 안심해도 됩니다. 현재 이들 뷰에서는 구독만 표시할 수 있습니다. SQL Server 2005에서는 항상 알림이 과도한 양의 리소스를 가져와 이를 제거하도록 "결정"할 수 있음을 기억하시기 바랍니다.

결론

쿼리 알림은 SQL Server 2005에 기본적으로 제공되는 강력하고 새로운 기능으로, ADO.NET 2.0과 ASP.NET 2.0에서 바로 사용할 수 있습니다. ADO.NET 기능(SqlNotificationRequestSqlDependency)은 SQL Server 2005 데이터베이스에 대해서만 작동하지만, ASP.NET에서는 폴링을 사용하는 대체 메커니즘을 통해 이 기능을 "이전 버전과도 호환하여" 사용할 수 있습니다. 이 기능은 구문 및 관련된 영향을 염두에 두고 신중하게 사용해야 합니다. 특히 이 기능의 핵심 부분은 ASP.NET에서 사용되는 읽기 위주 테이블입니다. 이러한 테이블은 다른 응용 프로그램은 물론 웹 응용 프로그램에서도 업데이트할 수 있습니다. 이 시나리오에서 쿼리 알림은 프로그래머들이 수년간 기다려 온 솔루션을 제공하고 있습니다.

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari
2006/04/20 21:46

출처 -MSDN

 

 

사용자 삽입 이미지
ADO.NET 2.0의 DataSet 및 DataTable


ADO.NET 2.0은 ADO.NET 1.x의 핵심 클래스에 몇 가지 흥미로운 기능 향상을 추가하고 성능, 융통성 및 효율을 높이는 다양한 새 클래스를 도입했습니다. 또한 새 일괄 업데이트 프로세스가 개선된 것을 비롯하여 ADO.NET 2.0 사전 베타 버전과 베타 버전의 수명 주기 동안 몇 가지 중요한 부분이 변경되었습니다. 접근 속도가 빨라지고 기능 집합의 안정성이 향상된 ADO.NET 2.0 최종 릴리스에 대한 자세한 설명을 시작하겠습니다.

이번 달에는 DataSet 및 DataTable 클래스의 개선 사항을 검토하는 것으로 시작하여 이러한 기능 향상이 가지는 의미와 사용 가능한 상황에 대해 알아보겠습니다. ADO.NET 1.x에서는 특히 행 집합을 대량으로 작업할 때 성능 문제가 발생할 수 있습니다. 여기서는 인덱싱 엔진의 변화를 통해 ADO.NET 2.0에서 이러한 성능 문제를 어떻게 해결했는지 설명하겠습니다. 또한 DataTable 클래스에 추가된 기능을 비롯하여 새 Load 메서드를 통한 로딩 옵션 및 행의 상태를 변경하는 새 메서드를 살펴보겠습니다. 이 칼럼의 다음 기사에서는 일괄 업데이트 기능 및 이진 serialization을 사용하여 전송할 DataSet를 압축하는 기능 등과 같은 다른 개선 사항에 대해 설명할 예정입니다.


DataTable 기능 향상

ADO.NET 1.x에서는 DataSet이 모든 주목을 받은 탓에 DataTable의 기능이 빛을 잃었습니다. 하지만 그렇다고 해서 DataTable이 유용한 클래스가 아니었다는 말은 아닙니다. DataTable은 행과 열의 컨테이너이며 모든 연결이 끊어진 데이터의 중심지로 생각할 수 있습니다. 하지만 DataSet은 DataRelation 및 DataTable 개체를 포함할 수 있기 때문에 많은 주목을 받았습니다.

ADO.NET 1.x에서 DataTable은 유용하기는 하지만 DataSet에는 없는 몇 가지 제약을 가지고 있습니다. 예를 들어, DataSet은 한 DataSet 내에서 두 개의 DataTable 개체를 병합할 수 있는 Merge 메서드를 제공하지만 DataTable은 Merge 메서드를 제공하지 않습니다. 따라서 DataSet 내에 포함되어 있지 않은 DataTable을 가지고 있고 이를 다른 DataTable 개체와 병합하려는 경우,그림 1에서처럼 먼저 DataSet 개체를 만들고 첫 번째 DataTable을 여기에 넣은 다음 DataSet.Merge 메서드를 호출해야 합니다.

이는 어렵지는 않지만 번거로운 일입니다. 하지만 이제는 ADO.NET 2.0의 DataTable 개체에 Merge 메서드가 있으므로 다음과 같이 두 DataTable 개체를 병합할 수 있습니다. dtCust1.Merge(dtCust2);

ADO.NET 1.x의 또 다른 불편 사항 중 하나는 반드시 DataSet과 연결한 후에야 DataTable에서 기본적인 XML 작업을 수행할 수 있다는 점입니다. 예를 들어, DataTable을 XML로 작성하려는 경우 DataTable을 DataSet으로 로드하고 DataSet의 WriteXml 메서드를 사용해야 합니다. 하지만 ADO.NET 2.0에서는 DataTable에 WriteXml 메서드가 있어 그럴 필요가 없습니다. ADO.NET 2.0의 DataTable에서는 WriteXml 메서드는 물론 ReadXml, ReadXmlSchema 및 WriteXmlSchema 메서드도 제공합니다

DataSet에도 몇 가지 새 메서드와 속성이 추가되었습니다. 실제로 이제 DataSet과 DataTable은 모두 Load 및 CreateDataReader 메서드뿐 아니라 RemotingFormat 속성까지 제공합니다. RemotingFormat 속성은 DataTable 또는 DataSet을 이진 또는 XML 형식으로 serialize할지 여부를 나타내는 데 사용됩니다. Load 메서드는 다양한 방법을 통해 데이터를 DataTable 또는 DataSet으로 로드하는 데 사용할 수 있습니다. 이에 대해서는 잠시 후 다시 설명하겠습니다.


간단하고 빠른 반복

DataTable의 CreateDataReader 메서드(이전 베타 버전의 GetDataReader)는 ADO.NET 2.0 DataTableReader의 인스턴스를 만듭니다. DataTable.CreateDataReader를 사용하여 작성한 DataTableReader는 DataTable과 동일한 행과 열을 제공합니다. DataTableReader가 DataSet 또는 DataTable의 CreateDataReader 메서드를 통해 만들어지면 DataTableReader에는 삭제된 행을 제외하고 컨테이너 개체의 모든 행이 포함됩니다.

DataTableReader는 DataReader(SqlDataReader)와 달리 DataTable보다 간단한 개체일 뿐 아니라 연결이 끊어져 있습니다. 이러한 특징은 DataReader와 마찬가지로 빨리 반복할 수 있는 간단한 개체를 얻을 수 있는 동시에 DataReader와 달리 데이터 원본과 연결이 끊어져 있으므로 유용한 역할을 합니다. DataTableReader는 기본 테이블의 행에 대한 iterator로 간주할 수 있으며 테이블 내용의 foreach 열거형과 유사합니다. 하지만 열거 중에 컬렉션에 행이 추가되거나 삭제되면 예외가 발생하는 테이블 행 열거와는 달리, DataTableReader는 기본 테이블에 대한 변경 사항을 탄력적으로 해결하며 발생하는 수정 사항을 고려하여 스스로 올바른 위치를 찾게 됩니다.

다음 예제에서는 DataTableReader를 만들어 DataGridView에 바인딩하는 방법을 보여 줍니다. using (SqlConnection cn = new SqlConnection(cnStr)){ SqlCommand cmd = new SqlCommand(sqlAllCustomers, cn); SqlDataAdapter adpt = new SqlDataAdapter(cmd); DataTable dtCustomers = new DataTable("Customers"); adpt.Fill(dtCustomers); DataTableReader dtRdr = ds.CreateDataReader(); dgvCustomers.DataSource = dtRdr;}

DataTableReader는 DataReader처럼 앞으로만 이동할 수 있습니다. 또한 DataReader와 마찬가지로 첫 번째 행으로 이동하려면 DataTableReader의 Read 메서드를 사용합니다. DataTableReader가 여러 DataTable이 포함된 DataSet에서 만들어진 경우, DataTableReader에는 여러 개의 결과 집합이 포함됩니다(DataTable당 하나). 또한 DataReader와 유사하게 NextResult 메서드를 호출하면 DataTableReader를 사용하여 각 후속 결과 집합에 액세스할 수 있습니다.

그림 2에서는 두 DataTable 개체가 포함된 DataSet에서 DataTableReader를 만드는 방법을 보여 줍니다. DataSet에 두 개의 DataTable이 있으므로 DataTableReader에도 두 개의 결과 집합이 포함됩니다.그림 2에서처럼 Read 및 NextResult 메서드를 사용하여 DataTableReader에서 두 결과 집합을 반복할 수 있습니다. 여기서 DataTableReader는 앞으로만 이동한다는 점에 유의합니다. 따라서 DataTableReader를 다시 반복하기 위해 DataTableReader에 두 번 액세스하려는 경우, 처음에 해당 레코드를 모두 읽은 후 다시 로드해야 합니다.

그림 2에서는 DataSet에서 CreateDataReader 메서드를 사용하여 DataTableReader를 만듭니다. DataTable의 결과 집합이 DataTableReader에 추가되는 순서는 DataSet에 나타나는 순서와 같습니다. DataTable 결과 집합의 순서를 지정하려는 경우에는 오버로드된 다른 CreateDataReader 메서드를 사용할 수 있습니다.


데이터 로드

DataTableReader는 DataTable 또는 DataSet을 채우는 데에도 사용할 수 있습니다. 실제로 DataSet 또는 DataTable의 새 Load 메서드를 사용하면 DataTableReader 또는 IDataReader 인터페이스를 구현하는 판독기 클래스를 전달할 수 있습니다. 다음 예제에서는 스키마와 몇 개의 행이 포함된 dt1이라는 DataTable이 있는 것으로 가정합니다. 이 예제에서는 dt1이라는 DataTable에서 DataTableReader를 만든 다음 곧바로 동일한 데이터가 있는 두 번째 DataTable(dt2라고 함)을 로드합니다. DataTableReader dtRdr = dt1.CreateDataReader();DataTable dt2 = new DataTable();dt2.Load(dtRdr);

ADO.NET 1.x에서는 DataAdapter의 Fill 메서드를 사용하여 DataSet 또는 DataTable을 채울 수 있었습니다. 또 다른 방법으로는 DataSet의 ReadXml 메서드를 사용하여 XML에서 DataSet을 채울 수도 있었습니다. 하지만 ADO.NET 2.0에는 Load 메서드가 도입되어 DataTableReader 또는 SqlDataReader처럼 IDataReader를 구현하는 클래스에서 DataSet 또는 DataTable을 로드할 수 있게 되었습니다. Load 메서드를 사용하여 여러 행을 로드하는 경우에는 먼저 BeginLoadData 메서드를 호출하여 알림, 인덱스 유지 관리 및 제약 조건 확인을 해제한 후 EndLoadData 메서드를 호출하여 다시 설정할 수 있습니다. ADO.NET 1.x에서도 사용할 수 있는 이들 메서드는 ADO.NET에서 행이 끝날 때마다 중지하고 인덱스를 다시 계산하거나 알림을 호출하거나 제약 조건을 확인할 필요가 없으므로 데이터를 보다 빨리 로드할 수 있습니다. 여러분은 단지 이러한 기능을 다시 설정하는 것만 기억하면 됩니다.


LoadOption 열거자

Load 및 Fill 메서드에는 LoadOptions 열거형 값 중 하나를 허용하는 오버로드가 있습니다. 이러한 설정 덕분에 DataSet 또는 DataTable에 Fill 또는 Load 작업을 수행하는 동안 기존 행에서 몇 개의 행을 교체해야 하는지를 나타내는 데 사용할 수 있는 강력한 기능을 얻게 되었습니다. 이 프로세스에서는 행을 교체하거나 추가하는 방법을 결정하는 데 기본 키를 사용하므로 기본 키가 설정되어 있는 것으로 가정합니다. 이 열거자 값은 현재 값 및/또는 원래 값을 들어오는 행의 값으로 덮어쓸지 여부를 결정하는 데 도움이 됩니다.그림 3에서는 세 가지 옵션과 그에 대한 간략한 설명을 보여 줍니다.

이러한 각 옵션은 상황에 따라 적절하게 응용 프로그램에서 사용됩니다. OverwriteChanges 옵션은 데이터가 포함된 DataTable이 있지만 데이터베이스에 있을 수 있는 변경된 값을 가져오려는 경우 유용합니다. 이 옵션을 사용하면 DataTable의 원래 값과 현재 값을 모두 데이터베이스의 값으로 덮어쓰게 됩니다. 여기서 중요한 점은 OverwriteChanges를 사용하는 경우 새 행이 추가되는 과정에 첫 번째 DataTable에서 수정된(원래 버전 또는 현재 버전) 데이터를 덮어쓰게 된다는 것입니다.

DataSet 열에서는 원래 값과 현재 값을 저장합니다. PreserveChanges는 원래 값을 덮어쓰면서 현재 값은 그대로 유지합니다. Upsert는 이와 반대로 현재 값을 덮어쓰면서 원래 값은 그대로 유지합니다.

다음은 PreserveChanges가 유용한 경우의 예입니다. Peggy라는 사용자가 화면을 열고 DataSet에서 고객의 DataGrid를 로드했다고 가정하겠습니다. Peggy는 CustomerID가 ALFKI인 고객의 도시를 베를린에서 뉴욕으로 수정했지만 저장 단추를 클릭하지 않은 채 커피를 마시러 나갔습니다. 그러는 동안 Katherine은 같은 고객의 도시를 베를린에서 마이애미로 수정했습니다. 이제 Peggy가 휴식을 마치고 돌아와 레코드를 저장하면 데이터 동시성 문제가 발생합니다. 따라서 이 상황에서 Peggy의 고객 레코드에 대한 원래 값은 베를린이었는데 뉴욕으로 변경했으므로 현재 값은 뉴욕이 됩니다. 하지만 데이터베이스의 도시는 이제 마이애미입니다. Peggy의 DataSet에 대한 원래 값을 데이터베이스에 있는 값으로 다시 설정하려는 경우, 데이터베이스에서 DataTableReader로 데이터를 가져온 다음 LoadOptions.PreserveChanges를 사용하여 DataSet으로 로드할 수 있습니다.그림 4에서는 이러한 실행 방법을 보여 줍니다.

이러한 설정의 사용 방법을 직접 확인하려면 디버거에서 이 코드를 단계별로 실행해 보십시오. Switch-case 문 바로 앞에 나타나는 선택 변수를 변경하면 다른 LoadOption 열거자를 시험해 볼 수 있습니다.그림 4에 나와 있는 코드에서는 출력 창에 지정된 열의 원래 버전과 현재 버전만 표시하는 ShowVersions 메서드를 호출합니다.


RowState 변경

행의 상태는 DataAdapter의 Update 메서드가 호출되면 업데이트, 삽입 또는 삭제될 행을 결정하는 데 도움이 되는 주요 요소입니다. 또한 GetChanges 메서드에서는 가져올 행을 결정하기 위해 RowState를 검사합니다. DataSet의 값을 변경하면 ADO.NET에서는 Modified, Added 또는 Unchanged 같은 RowState 값 중 하나로 이를 간접적으로 설정하여 RowState 설정을 자동으로 처리합니다.

이러한 기능은 이따금 행의 상태를 직접 설정하는 데 매우 유용합니다. 이 경우 DataRow의 새 SetAdded 및 SetModified 메서드를 통해 작업을 좀 더 수월하게 수행할 수 있습니다. 예를 들어, ADO.NET을 사용하여 한 데이터베이스에서 다른 데이터베이스로 몇 개의 행을 복사해야 하는 상황을 가정해 보겠습니다. ADO.NET 2.0에서는 DataAdapter의 Fill 메서드를 사용하여 데이터베이스의 DataTable을 채우고 행의 RowState 설정을 Added로 변경한 다음, 이를 두 번째 DataAdapter를 사용하여 추가될 두 번째 데이터베이스(같은 스키마를 사용한다고 가정)로 보낼 수 있습니다. 이렇게 되면 각 행의 SetAdded 메서드를 호출하여 행의 RowState를 Unchanged에서 Added로 변경할 수 있습니다.

이러한 메서드의 작동 방식을 설명하기 위해 필자는그림 5의 코드에 나와 있는 다른 예제를 포함시켰습니다. 이 코드에서는 고객의 행 집합을 검색한 다음 한 행의 RowState 설정을 Modified로 설정하고 다른 행의 설정은 Added로 설정하고 있습니다. 그런 다음 필자는 DataTable의 GetChanges 메서드를 사용하여 RowState가 Modified인 행이 포함된 DataTable을 만들고 행 수를 저장합니다. 그리고 추가된 행 수를 가져와 MessageBox를 사용하여 표시합니다.

DataRow의 SetAdded 및 SetModified 메서드는 변경되지 않은 행에 대해서만 실행됩니다. 이러한 특징이 유용한 또 다른 상황은 웹 서비스에서 DataSet 또는 DataTable을 수신하고 행이 모두 Unchanged로 표시되는 경우입니다. DataTable에 따라 데이터베이스에 추가 또는 업데이트 작업을 수행하려는 경우 이러한 새 메서드를 사용하여 RowState를 설정할 수 있습니다. 그렇지 않고 RowState를 Unchanged로 두는 경우에는 DataAdpater의 Udpate 메서드에서 행을 UpdateCommand 또는 InsertCommand로 보내지 않습니다.


0 ~ 60

ADO.NET 2.0에 새로 도입된 가장 유용한 기능은 새로운 메서드나 클래스가 아니라 성능이 집중적으로 향상된 부분에 있습니다. DataSet 및 DataTable의 큰 단점은 행의 수가 많은 경우(100, 1,000, 10,000 또는 그 이상) 로드 속도가 매우 느려진다는 점입니다. 규모가 큰 DataTable을 이동하려는 경우에는 상황이 아주 힘들어질 수 있습니다. 이러한 실질적인 제한 사항을 해결하기 위해 ADO.NET 2.0에서는 인덱싱 엔진의 속도를 높이는 데 많은 노력을 기울였습니다. ADO.NET에서는 인덱싱 엔진을 다시 작성하는 작업을 통해 DataTable의 로드 및 병합을 비롯한 모든 영역에서 성능이 대폭 개선되었습니다.그림 6에서는 Visual Studio .NET 2003(ADO.NET 1.1 사용) 또는 Visual Studio 2005(ADO.NET 2.0 사용)에서 실행할 수 있는 몇 가지 샘플 코드를 보여 줍니다. 필자는 행 수를 변경해 가며 두 환경 모두에서 이 코드를 비교해 보았습니다.

그림 6의 코드에서는 DataTable을 만들고 여기에 두 개의 열을 추가한 다음 반복할 때마다 DataTable에 행을 추가하여 1백만 번을 반복합니다. 반복이 완료되면 MessageBox 메서드를 통해 경과된 시간(초)이 사용자에게 표시됩니다. 필자는 이 테스트를 ADO.NET 1.1 및 ADO.NET 2.0 모두에서 다양한 반복에 대해 수행했습니다. 테스트 결과는그림 7에 나와 있습니다.

속도는 DataTable에 제약 조건이 없는 경우가 훨씬 더 좋았습니다. 예를 들어, Unique 제약 조건을 제거하자 두 버전의 환경에서 단 1초만에 1백만 개의 행을 로드할 수 있었습니다. 물론 열을 두 개만 로드했다는 점과 SomeNumber 열 값이 단순히 순차적으로 감소하는 정수였다는 점을 무시할 수 없습니다. 여러분의 결과는 제 경우와 다르겠지만 중요한 부분은 ADO.NET 2.0의 인덱싱 엔진 속도가 매우 빨라졌다는 것입니다. 이제는 DataTable을 사용하여 백만 개의 행을 포함시키는 일이 완연한 현실입니다.


요약

ADO.NET 2.0에서는 많은 수의 행을 로드하는 것 같이 이전 버전에서 문제가 되었던 여러 부분의 기능이 향상되었습니다. 또한 여러 가지 새로운 기능이 추가되어 개발 작업이 더욱 용이해졌습니다. DataTable 클래스에는 DataSet 클래스에 이미 존재했던 여러 메서드가 추가되었으며 새 DataTableReader 클래스도 새롭게 도입되었습니다. Data Points 칼럼의 다음 기사에서는 이진 serialization을 통한 성능 향상 방법, 일괄 업데이트 활용 방법, 새 DataView 기능, 새 SqlConnectionBuilder 클래스 등을 살펴보며 ADO.NET 2.0에 대한 설명을 이어가겠습니다.

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari
2006/01/18 23:01

 

개발 팁

 

ANSI JOIN 및 이전의 OUTER JOIN 구문
집합 기반으로 특정 행을 검색하는 방법
대량 복사 프로그램 유틸리티
재컴파일을 방지하도록 소유자 접두사 코딩
단방향 병합 복제 구성
Access 데이터를 XML로 변환
계산 열에서 UNIQUE 및 PRIMARY KEY 제약 조건 만들기
차단된 연결 검색
종속성 표시
업데이트를 여러 일괄 처리로 분리
쿼리 최적화 프로그램에서 통계를 사용하는 방법
XML 파일 가져오기
WHERE 절에 변수 포함시키기
인라인 또는 포함된 SQL 및 저장 프로시저
SQL Server 2000, 7.0 및 6.x에서 char 값 관리
쿼리 결과 임의 정렬
Visual Basic 6.0에서 SQL Server로의 연결 테스트
Excel로 내보낼 때 데이터 자르기
문자열 IP 주소를 바이너리 값으로 변환
TOP N 절 및 SET ROWCOUNT N
뷰에서 ORDER BY 사용
Transact-SQL을 사용하여 SQL Server 프로세서 개수 세기
예/아니오 문자열

 

 

관리 팁

 

복제된 테이블에 열 추가
자동 닫기 및 자동 축소 옵션 방지
SQL Server 2000 Enterprise Edition 사용 이점
대/소문자 구분 비교
SQL Server 7.0에서 SQL Server 2000으로 코드 페이지 변경
데이터베이스 소유자(dbo) 계정을 시스템 관리자(sa)로 변경
포트 번호 검사
SQL Server 통계 수집
SQL Server의 메일 프로필 구성
Autogrowth는 업데이트를 차단합니까?
정렬 순서가 성능에 영향을 줍니까?
NULL이 아닌 모든 값의 고유성 보장
고스트 레코드 정리 및 오류 602
데이터베이스 개체에 대한 권한 부여
수평 분할 병합 게시
Oracle 연결 서버 추가 방법
SQL Server 2000 설치가 32비트 또는 64비트인지 확인하는 방법
시스템 관리자 계정을 사용하여 여러 개발자를 관리하는 방법
SQL Server 계정 잠김을 어떻게 복구합니까?
SQL Server를 사용하여 Microsoft Word 문서 가져오기 및 인덱싱
날짜 오류 조사
로그 파일 증가 및 DBCC DBREINDEX
결과 집합 페이징 관리
다른 드라이브로 트랜잭션 로그 이동
명명된 인스턴스 검색을 위한 UDP 포트 1434 열기
로그 전달 역할 변경 수행
실패한 노드에 SQL Server 다시 설치
트랜잭션 로그 파일에서 트랜잭션 롤백
보조 컴퓨터에서 DTS 작업 실행
SQL Server와 같은 서버에서 바이러스 검색 소프트웨어 실행
SQLAgentCMDExec 권한
SQL Server 프로파일러 및 매개 변수화된 문
인덱스 작성 중 테이블 액세스
한 테이블에서 다른 테이블로 열 업데이트
확장 속성을 사용하여 데이터 사전 만들기
SQL Server 시스템을 PDC로 사용
뷰를 사용하여 데이터에 대한 사용자 액세스 제어

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari
2005/06/28 03:40

출처 :마이크로소프트

"SQL Server가 없거나 액세스가 거부되었습니다."라는 오류 메시지의 잠재적 원인

"SQL Server가 없거나 액세스가 거부되었습니다."라는 오류 메시지는 Microsoft SQL Server를 실행하는 컴퓨터에 연결할 수 없음을 나타내는 일반 Microsoft Data Access Components(MDAC) 메시지입니다. 이 일반 메시지의 원인은 여러 가지가 있습니다. 가장 일반적인 원인 중 몇 가지가 "추가 정보" 절에 설명되어 있습니다.

참고다음과 유사한 오류 메시지의 원인도 이와 비슷합니다.
SQL Server를 사용할 수 없거나 존재하지 않습니다.
지정한 SQL Server이(가) 없습니다.
 

추가 정보

"SQL Server가 없거나 액세스가 거부되었습니다."라는 메시지는 다음 사항을 나타내지않습니다.
SQL Server에 로그온하지 못했습니다.
해당 SQL Server에 올바른 사용 권한이 없어 쿼리를 처리하지 못합니다.
Windows 인증만 허용되므로 SQL Server 인증을 사용할 수 없습니다.

잠재적인 원인

서버 관련 원인

클라이언트 컴퓨터가 SQL Server를 실행하는 컴퓨터에 연결할 수 없는 경우 서버쪽에 문제가 있을 수 있습니다.
연결 문자열에서 지정한 컴퓨터에 SQL Server가 설치되어 있지 않습니다. 연결 문자열에서 지정한 컴퓨터에 SQL Server가 실제로 설치되어 있는지 확인합니다.
SQL Server가 시작되지 않았습니다. 연결을 받아들이려면 SQL Server가 실행 중이어야 합니다.
SQL Server가 연결할 때 사용하는 프로토콜이나 포트에서 수신 대기하고 있지 않습니다. 서버의 서버 네트워크 유틸리티가 SQL Server에서 사용할 프로토콜과 SQL Server에서 수신 대기할 TCP/IP 포트를 결정합니다. 클라이언트 네트워크 유틸리티, MDAC DSN(데이터 원본 이름) 또는 연결 문자열이 클라이언트가 연결할 때 사용하는 프로토콜과 포트를 결정합니다. 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
289573PRB: SQL Server Net-Library로 DSN 구성
328383INF: SQL Server 클라이언트가 연결하려 할 때 프로토콜을 변경할 수 있다
SQL Server가 시작되면 지정된 TCP/IP 포트에서 수신 대기하지 못하고 TCP/IP 연결을 받아들이지 않습니다. 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
293107PRB: 다른 응용 프로그램에서 사용 중인 TCP/IP 포트
SQL Server 이름이 컴퓨터 이름과 다릅니다. SQL Server 2000의 경우 이 문제에 대한 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
303774BUG: SQL Server 온라인 설명서의 '서버 이름 바꾸기' 항목이 불완전하다
SQL Server 7.0의 경우 이 문제에 대한 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
195759INF: 자주 문의되는 질문들(FAQ's) - SQL 설치
클러스터된 환경에 있는 SQL Server의 경우 이 문제에 대한 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
307336INF: 클러스터된 SQL Server 네트워크 이름을 변경하는 방법
자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
298822FIX: MSDE 연결이 멀티홈 컴퓨터에서 실패할 수 있다
자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
306199FIX: 동적 IP 주소가 SQL Server 2000에 연결할 수 없다
SQL Server Desktop Engine(MSDE 2000이라고도 함) 서비스 팩 3(SP3) 또는 SP3a부터는 기본적으로 로컬 연결만 허용됩니다. SQL Server Desktop Engine과의 원격 연결을 허용하려면 다음 문서에 나와 있는 단계를 수행하십시오.
814130INF: SQL Server 2000 로컬 데이터베이스에 대한 네트워크 연결을 보호하는 방법

클라이언트 또는 응용 프로그램 관련 원인

일부 클라이언트 컴퓨터나 응용 프로그램은 연결할 수 있지만 다른 클라이언트 컴퓨터나 응용 프로그램은 연결할 수 없는 경우 클라이언트쪽 문제일 수 있습니다.
컴퓨터 이름이 없습니다. 연결할 컴퓨터 이름의 맞춤법을 두 번 검사합니다.
SQL Server의 명명된 인스턴스에 액세스하려고 하지만 올바른 인스턴스 이름을 지정하지 않았습니다. 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
265808SQL Server 2000 명명된 인스턴스를 연결하는 방법
연결할 때 사용할 Windows 수준의 보안이 기대한 것과 다릅니다. 예를 들어, Microsoft 인터넷 정보 서비스(IIS)는 Windows에 로그온할 때 사용한 계정이 아니라IUSR_computername이나 IIS를 구성한 방법에 따라 다른 계정을 사용합니다. 또한, 연결된 서버가 일부 상황에서는 SQL Server 에이전트의 Windows 수준 보안 컨텍스트를 사용합니다. 사용 중인 계정의 보안 컨텍스트에 SQL Server가 설치되어 있는 컴퓨터에 대한 액세스 권한이 없을 수 있습니다.
올바른 프로토콜을 사용하고 있지 않습니다. CNU(클라이언트 네트워크 유틸리티)를 검사하여 사용할 프로토콜을 확인합니다. 이전 버전의 MDAC에서는 프로토콜을 사용 가능하게 설정하는 대신 기본값을 설정합니다. 또한 CNU에 예상한 것과 다른 프로토콜이나 포트(또는 둘 모두)를 지정하는 정의된 별칭이 있는지 확인합니다. 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
328383INF: SQL Server 클라이언트가 연결하려 할 때 프로토콜을 변경할 수 있다
289573PRB: SQL Server Net-Library로 DSN 구성
클라이언트의 네트워크 속성에서 Microsoft 네트워크용 클라이언트 네트워킹 구성 요소를 제거했습니다. 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
253959네트워크 어댑터에서 언바운드되어도 Microsoft 네트워크용 클라이언트가 기능한다
SQL Server가 클러스터된 경우 네트워크 어댑터의 이름이 잘못 지정되었거나 네트워크 어댑터가 제대로 구성되지 않았을 수 있습니다. 네트워크 어댑터를 특수 문자나 대소문자를 혼합하여 이름을 지정한 경우 이름을 바꿉니다. 컴퓨터의 네트워크 어댑터가 제대로 구성되었는지 확인합니다.
MDAC 불일치의 부작용이 발생했을 수 있습니다. 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
307255구성 요소 검사기: 문제 진단 및 MDAC 설치 다시 구성
다음 하위 키에 지정되어 있는 프로토콜을 확인합니다.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\ConnectTo\DSQUERY
이 값은 일반적으로 CNU의 설정을 반영하지만 그렇지 않을 때도 있습니다. 값이DBNETLIB이면 해당 CNU의사용할 수 있는 프로토콜목록에 나와 있는 프로토콜 중 하나를 사용합니다. 특정 프로토콜이 목록에 나와 있으면 그 프로토콜이 대신 사용됩니다.
명명된 파이프를 통해 연결하려 하지만 SQL Server에서 수신 대기하는 명명된 파이프가 기본값에서 변경되었거나 잘못된 파이프 이름을 사용하고 있습니다.

기본 인스턴스의 경우 서버 네트워크 유틸리티에서 다음 파이프를 사용합니다.
\\.\pipe\sql\query
명명된 인스턴스의 경우 다음을 사용합니다.
\\.\pipe\MSSQL$instancename\sql\query
올바른 유형(파일, 사용자 또는 시스템)이 아닌 DSN을 지정했습니다. 다른 유형의 DSN에 대한 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
213772시스템, 사용자 및 파일 데이터 원본을 사용하는 방법
자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
290820BUG: SQL Desktop Edition: MDAC 2.6을 설치한 후 공유 메모리가 연결하지 못한다
 

네트워크 또는 도메인 관련 원인

특정 도메인 또는 특정 서브넷에 있거나 방화벽 뒤에 있는 컴퓨터에서만 이 오류 메시지가 나타나는 경우에는 다음과 같은 문제를 검토하십시오.
방화벽이나 라우터가 제대로 구성되지 않았거나 UDP 포트 1434를 차단하도록 구성되었습니다. UDP 포트 1434가 차단된 경우 클라이언트의 연결 문자열에서 SQL Server가 수신 대기하는 포트를 지정해야 합니다. 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
287932INF: 방화벽을 통해 SQL Server와 통신하는 데 필요한 TCP 포트
318432BUG: 방화벽을 통해 클러스터된 명명된 인스턴스에 연결할 수 없다
Windows 계정에 대한 로컬 보안 정책 사용자 권한 할당이 네트워크에서의 액세스를 허용하지 않습니다. "네트워크에서 이 컴퓨터 액세스" 정책이 허용되어야 합니다. 또한, "네트워크에서 이 컴퓨터 액세스 거부" 권한이 부여되지 않아야 합니다.
도메인 트러스트 문제가 있거나 컴퓨터 중 하나가 작업 그룹에 있으므로 해당 도메인에 액세스할 수 없습니다. 어떤 경우에는 이 문제를 해결하기 위해 각 계정에 같은 암호를 사용하여 두 컴퓨터 모두에서 동일한 로컬 Windows 계정을 만들 수 있습니다. 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
321247HOWTO: 로그 전달 보안 구성
이름 확인이 작동하지 않습니다. DNS, WINS, 호스트 또는 lmhost와 같은 사용 중인 이름 확인 시스템의 설정을 확인합니다. 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
169790기본 TCP/IP 문제를 해결하는 방법
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari
2005/06/24 00:43
MSDN Library 뷰어 도움말 

Microsoft SQL Server 쿼리 프로세서 내부 및 아키텍처

Hal Berenson, Kalen Delaney

2000년 1월

요약:이 문서는 클라이언트에서 Microsoft SQL Server 쿼리를 처리하는 방법, 여러 클라이언트와 SQL Server가 상호 작용하는 방법, 클라이언트 요청을 처리하기 위한 SQL Server 작업을 설명합니다(26페이지/인쇄 페이지 기준).

소개

Microsoft® SQL Server™의 내부 및 아키텍처는 광범위한 주제이므로 이 문서에서는 개발자와 관련된 분야로 한정하여 다른 글에서 자세히 설명되어 있지 않은 항목들을 중심으로 알아보겠습니다. SQL Server의 아키텍처를 논의하면서 클라이언트에서 작업이 처리되는 방법, 여러 클라이언트와 SQL Server가 상호 작용하는 방법, 클라이언트 요청을 처리하기 위해 SQL Server가 하는 작업 등에 대해 중점적으로 살펴봅니다. SQL Server의 다른 측면을 설명하는 글도 있습니다. 이 중 Microsoft Press에서 출판한 Ron Soukup과 Kalen Delaney의 "Inside SQL Server 7.0"은 저장소 엔진의 메커니즘 및 작동에 관한 내용을 아주 상세하게 다루고 있지만 쿼리 프로세서는 그다지 자세하게 다루지 않았습니다. 이 문서에서는 이와 같이 다른 글에서 자세하게 다루지 않는 부분을 상세히 설명합니다.

이 정보를 통해 더 나은 응용 프로그램을 작성할 수 있게 되기를 바랍니다. 이 문서를 읽으면 새로운 관점과 이해를 통해 성능 문제를 바라볼 수 있을 것입니다.

클라이언트/서버 시스템인 SQL Server

오랫동안 SQL Server는 클라이언트/서버 시스템으로 알려졌습니다. 사실 SQL Server의 토대가 된 Sybase DataServer는 초기에 클라이언트/서버 시스템으로 개발된 최초의 상업용 관계형 데이터베이스 시스템이었습니다. 그러나 그 의미는 SQL Server가 2계층 시스템이라는 것 이상을 나타냅니다. 일반적으로 2계층 시스템은 하나의 컴퓨터에서 클라이언트 응용 프로그램이 실행되고 다른 컴퓨터에 있는 서버에 요청을 보내는 것을 의미합니다. SQL Server에서 클라이언트/서버는 서버 구성 요소 자체와는 별개이며 프로세스 구조에서 원격에 위치하는 클라이언트 API의 일부분인 SQL Server부분입니다.

기존의 2계층 모델에서 클라이언트 부분은 대량의 클라이언트 응용 프로그램 논리 및 업무 논리와 함께 데스크톱 컴퓨터에 위치하며 데이터베이스 시스템에 대한 요청을 직접 생성합니다. 그런 다음 클라이언트는 요청에 대한 응답으로 서버에서 데이터를 받습니다.

3계층 구조에서도 동일한 모델이 적용됩니다. 오랫동안 SQL Server는 20, 30년대의 고전적인 3계층 모델을 대표하는 BEA의 Tuxedo나 Compaq의 ACMSxp와 같은 트랜잭션 처리 모니터와 함께 사용되었습니다. Microsoft의 MTS와 새로운 COM+ 1.0 등의 응용 프로그램 서버에서 볼 수 있듯이 오늘날 대부분의 웹 기반 응용 프로그램은 3계층 모델을 사용하고 있습니다. SQL Server 측면에서 보면 3계층 솔루션의 클라이언트는중간계층에 위치하는 논리의 일부입니다. 이 중간 계층은 데이터베이스 시스템과 직접 상호 작용합니다. 실제 데스크톱이나 씬 클라이언트는 다른 메커니즘을 사용하여 데이터베이스 시스템이 아닌 중간 계층과 직접 상호 작용합니다. 그림 1에서는 이러한 구조를 보여 줍니다.

사용자 삽입 이미지

그림 1. 3계층 시스템 모델

클라이언트 아키텍처

아키텍처 측면에서 보면 SQL Server 관계형 서버 구성 요소에서는 클라이언트가 실행되는 위치가 크게 중요하지는 않습니다. 실제로 SQL Server가 실행되는 동일한 시스템에서 응용 프로그램을 실행하더라도 SQL Server 측면에서 보면 클라이언트/서버 모델과 다른 점이 없습니다. 서버에서는 별도의 다중 스레드 프로세스를 실행하므로 클라이언트의 위치에 관계 없이 클라이언트로의 요청을 처리합니다. 클라이언트 코드 자체는 클라이언트 응용 프로그램 내에서 실행되는 별개의 DLL이며 SQL Server에 대한 실제 인터페이스는 클라이언트와 서버 사이에서 통신하는 TDS(Tabular Data Stream) 프로토콜입니다.

"SQL Server의 원시 인터페이스는 무엇입니까?"라는 질문을 종종 받습니다. 오랫동안 많은 개발자들은 Sybase에서 개발한 클라이언트 API인 DB-Library가 SQL Server에 대한 원시 인터페이스라고 생각했으므로 ODBC 등의 인터페이스를 사용하지 않으려고 했습니다. 사실 SQL Server 관계형 서버 자체에는 원시 API가 없습니다. SQL Server 관계형 서버의 인터페이스는 클라이언트와 서버 간의 통신 스트림용 프로토콜인 TDS입니다. TDS는 서버에서 클라이언트로 다시 보낸 결과 집합과 클라이언트에서 서버로 보낸 SQL 문을 캡슐화합니다. TDS를 직접 처리하는 모든 API는원시SQL Server 인터페이스입니다.

이제 그림 2에 나타난 대로 클라이언트 구성 요소에 대해 알아보도록 합니다. SQL Server 자체의 범위를 벗어나는 클라이언트 아키텍처 부분은 언급하지 않겠지만 응용 프로그램을 작성하려면 이러한 부분들에 대해서 알고 있어야 합니다. 가장 많이 알려진 부분은 다양한 개체 모델입니다. ASP나 Microsoft Visual Basic® 응용 프로그램을 작성할 경우 ADO를 통해 데이터베이스 시스템과 상호 작용합니다. 이때 ODBC나 OLE-DB 등 낮은 수준의 API를 직접 호출하지 않습니다. ADO는 OLE-DB로 매핑되고 RDO는 ODBC로 매핑됩니다. 따라서 프로그래밍 모델에서 가장 많이 사용되는 이러한 개체 모델은 엄격하게 따지면 SQL Server의 클라이언트 아키텍처가 아닙니다. 이 수준에서 SQL Server 인프라 위에 놓일 수 있는 추가 구성 요소도 있습니다. OLE-DB의 Session Pooling Service Provider도 이러한 구성 요소 중 하나입니다.

사용자 삽입 이미지

그림 2. 클라이언트 아키텍처

클라이언트 인터페이스

SQL Server7.0, OLE-DB 및 ODBC의 원시 인터페이스로 생각할 수 있는 SQL Server에 대한 인터페이스가 두 가지 있습니다. TDS와 관련된 DB-Library 인터페이스도 원시 인터페이스이지만 DB-Library에서는 서버측에 변환이 필요한 이전 버전의 TDS를 사용합니다. SQL Server 7.0에 기존 DB-Library 응용 프로그램을 계속 사용하고 있지만 ODBC와 OLE DB를 사용해야 여러 새로운 기능을 접할 수 있고 성능을 향상시킬 수 있습니다. SQL Server 7.0의 새로운 기능을 지원하기 위해 DB-Library를 업데이트하면 기존 응용 프로그램과 호환되지 않는 문제가 여러 곳에서 발생하게 되며 이에 따라 응용 프로그램을 변경해야 합니다. 5년 전부터 새로운 SQL Server 응용 프로그램에 대한 기본 API로 DB-Library 대신 ODBC를 사용하고 있으므로 호환되지 않는 새 DB-Library 버전을 도입하는 것은 바람직하지 않습니다.

위의 그림 2에서 볼 수 있듯이 모든 클라이언트 API에는 세 가지 구성 요소가 있습니다. 가장 위에는 행 집합이 표시되는 모양, 커서 모양 등 API 관련 요소가 구현됩니다. TDS 포맷터에서 SQL 문과 같은 실제 요청을 받아들인 다음 TDS 메시지로 묶어 SQL Server로 보내고 결과를 되돌려 받은 다음 그 결과를 인터페이스 구현에 공급합니다.

또한 모든 공급자가 사용하는 공통 라이브러리 코드가 몇 가지 있습니다. 예를 들어, BCP 기능은 ODBC와 OLE-DB에서 호출하는 라이브러리입니다. DTC는 두 번째 예이고 제3의 예는 ODBC 표준 SQL 문으로 모든 공급자 간의 공통된 매개 변수 표식이 있는 CALL 구문입니다.

이전에 언급한 대로 DB-Library가 SQL Server 6.5 버전을 계속 사용한다는 한계를 제외하고 TDS 프로토콜은 모든 API에 대해 동일하게 적용됩니다. SQL Server 7.0에서 작업할 때 ODBC와 OLE-DB는 SQL Server 7.0 버전을 사용하지만 7.0 이전의 6.5나 6.0 서버에서도 작업할 수 있는 기능이 있습니다. 또한 클라이언트와 서버 모두 네트워크 추상화 인터페이스와 통신할 수 있으므로 IPX나 TCP/IP를 고려하지 않아도 되는 추상화 계층인 Net-Library도 있습니다. Net-Library에서 하는 작업에 대해서는 자세히 다루지 않습니다. Net-Library는 기본적으로 낮은 수준의 네트워크 통신 정보를 나머지 소프트웨어에서 숨기는 작업을 수행합니다.

클라이언트에서서버를보는관점

이미 언급한 대로 클라이언트에서는 주로 TDS 메시지를 사용하여 SQL Server와 통신합니다. TDS는 단순한 프로토콜로 SQL Server에서 메시지를 받으면 이벤트 발생으로 간주됩니다. 먼저 연결 상태에서 클라이언트가 로그인 메시지나 이벤트를 보낸 다음 이에 대한 성공이나 실패 응답을 받습니다. SQL 문으로 보내려면 SQL 언어 메시지를 사용하여 클라이언트가 패키지화하여 SQL Server로 보낼 수 있도록 합니다. 또한 저장 프로시저, 시스템 프로시저 또는 이후에 논의할 의사 시스템 저장 프로시저를 호출하려면 SQL Server의 RPC 이벤트에 해당하는 RPC 메시지를 클라이언트에서 보낼 수도 있습니다. 마지막 두 프로시저의 경우 서버에서는 토큰 데이터 스트림으로 결과를 되돌려 보냅니다. 이는 SQL Server 구성 요소 간의 사적 계약으로 간주되므로 Microsoft에서 실제 TDS 메시지를 문서화하지 않습니다.

카탈로그 저장 프로시저는 클라이언트/서버 상호 작용의 또 다른 핵심 요소입니다. 카탈로그 저장 프로시저는 SQL Server 6.0의 ODBC에서 처음 소개되었으며 sp_tables와 sp_columns 등의 프로시저를 포함합니다. ODBC와 OLE-DB API는 데이터베이스 개체에 대한 메타데이터를 기술하는 표준 방법을 정의합니다. 모든 종류의 RDBMS에 이러한 표준을 사용할 수 있어야 하므로 SQL Server의 각 시스템 테이블에 맞춰 표준이 조정되지는 않습니다. 클라이언트에서 시스템 테이블에 대한 여러 쿼리를 서버로 보내 클라이언트에 있는 메타데이터의 이러한 표준 뷰로 만드는 대신 시스템 저장 프로시저 집합이 서버에 만들어져 해당 API에 적절한 형식으로 정보를 반환합니다. 이렇게 함으로써 여러 중요한 메타데이터 요청이 단 한 번의 라운드트립으로 완료됩니다.

ODBC용으로 작성된 프로시저는 문서화되어 있으며 다른 메커니즘에서 제공되지 않는 시스템 테이블의 정보가 필요한 경우 이 프로시저를 유용하게 사용할 수 있습니다. 이를 통해 Transact-SQL 프로시저와 DB-Library 응용 프로그램은 SQL Server 시스템 테이블에 대한 복잡한 쿼리를 작성하지 않고도 메타데이터에 액세스하여 앞으로 Microsoft에서 시스템 테이블에 적용할 변경 내용이 응용 프로그램에 적용되지 않도록 할 수 있습니다.

OLE DB는 ODBC 메타데이터와 다르지만 유사한 점이 있는 일련의 스키마 행 집합을 정의합니다. 이러한 스키마 행 집합을 효율적으로 채우기 위해 새 카탈로그 저장 프로시저 집합이 만들어졌습니다. 그러나 저장 프로시저가 이전 기능을 복제하므로 이 새로운 저장 프로시저 집합은 문서화되지 않았습니다. 메타데이터를 구할 수 있는 기존의 여러 가지 방법을 사용할 수 있으므로 SQL Server 팀은 프로그래밍 모델에 도움이 되지 않는 기능을 문서화하지 않기로 결정했습니다.

클라이언트가 서버와 상호 작용하는 방법에 대해서는 제3의 측면이 있습니다. 이 개념은 SQL Server 6.0에서 처음 도입되었지만 크게 부각되지는 않았습니다. 이는 의사 시스템 저장 프로시저의 개념으로 SQL Server 7.0에서 매우 중요한 역할을 합니다. 처음 SQL Server 6.0에 서버 커서가 개발되었을 때 개발자들은 클라이언트/서버 상호 작용을 관리하는 방법을 선택할 수 있었습니다. 커서는 클라이언트에서 추가 SQL 문을 지정하지 않고 데이터 행이 반환되도록 하므로 사용 가능한 TDS 메시지 집합에 완벽하게 적합하지는 않았습니다. 개발자가 TDS 프로토콜에 메시지를 추가할 수는 있지만 그렇게 하려면 많은 다른 구성 요소를 변경해야 했습니다. SQL Server 6.0의 TDS 버전 또한 상호 운영성을 보장하기 위해 Sybase 버전에 가깝게 만들어졌으므로 개발자들은 대체 메커니즘을 사용하게 되었습니다. 이들은 저장 프로시저가 실제로는 단순히 SQL Server 코드 기준으로의 진입점임에도 새로운 서버 커서 기능이 시스템 저장 프로시저로 보이게 만들었습니다. 표준 RPC TDS 메시지를 사용하여 클라이언트 응용 프로그램에서 이를 호출합니다. 클라이언트에서 저장 프로시저처럼 호출되지만 다른 저장 프로시저처럼 단순한 SQL 문으로 이루어진 것이 아니므로 이 프로시저를 의사 시스템 저장 프로시저라고 합니다. 대부분의 이러한 의사 시스템 저장 프로시저는 개인 프로시저이고 문서화되지 않았습니다. 커서 프로시저의 경우 모든 API가자체의커서 API 모델 집합과 고유 커서 조작 함수를 보여 주므로 저장 프로시저 자체를 문서화할 필요가 없습니다. Transact-SQL 언어에도 DECLARE, OPEN, FETCH 등을 사용하여 커서를 보여 줄 수 있는 구문이 있으므로 내부적으로만 사용되는 sp_cursor와 같은 의사 시스템 저장 프로시저를 문서화할 필요가 전혀 없습니다.

ODBC와 OLE DB는 매개 변수화된 쿼리와 준비/실행 모델의 개념을 보여 줍니다. SQL Server 7.0 이전에는 클라이언트 API의 코드로 이러한 개념이 구현되었습니다. SQL Server 7.0에서 Microsoft는 관계형 서버에 이러한 개념에 대한 지원을 추가하여 새 의사 시스템 저장 프로시저를 통해 이러한 지원을 보여 줍니다. 이러한 기능과 서버에서 이 기능을 지원하는 방법에 대해서는 이 문서의 후반부에서 살펴봅니다. sp_executesql 프로시저를 통한 매개 변수화된 쿼리 지원은 직접 Transact-SQL 및 DB-Library를 사용할 때 유용하며 이 프로시저는 문서화되었습니다. 준비/실행 프로시저는 ODBC 드라이버 및 OLE DB 공급자에서만 사용됩니다.

SQL Server와 통신하는 모든 클라이언트는 TDS 프로토콜, 카탈로그 저장 프로시저, 의사 시스템 저장 프로시저의 세 가지 기능 집합을 기반으로 만들어졌습니다.

서버 아키텍처

SQL Server, 좀 더 구체적으로 SQL Server 관계형 서버는 일반적으로 관계형 엔진과 저장소 엔진의 두 부분으로 구성됩니다. 이미 언급한 대로 저장소 엔진에 대한 문서는 많이 있으므로 이 문서에서는 관계형 엔진의 기능을 중점적으로 설명합니다. 그림 3에서는 SQL Server 관계형 엔진 부분의 기본 구성 요소를 보여 줍니다. 그림의 구성 요소를 세 개의 하위 시스템 그룹으로 분류할 수 있습니다. 그림의 왼쪽은 쿼리 최적화 프로그램을 포함하여 쿼리 컴파일을 지시하는 구성 요소입니다. 최적화 프로그램은 모든 관계형 데이터베이스 엔진에서 가장 모호한 부분 중 하나이지만 성능 측면에서 볼 때 가장 중요한 부분이기도 합니다. 쿼리 최적화 프로그램에서는 SQL에 표현된 비프로시저 요청을 받아 들여 디스크 I/O, 필터링 및 이 요청을 효율적으로 만족시키는 다른 프로시저 논리로 변환합니다. 그림의 오른쪽은 실행 인프라이며 이 부분은 아주 작은 기능 집합입니다. 컴파일 구성 요소에서 해당 작업을 마치면 몇 가지 서비스를 제공함으로써 바로 실행할 수 있는 것을 만들어냅니다.

사용자 삽입 이미지

그림 3. 서버 아키텍처

그림의 중앙은 SQL 관리자라고 하는 부분으로 SQL Server 내의 모든 흐름을 제어합니다. SQL 관리자에서는 RPC 메시지를 처리하며 SQL Server 7.0에서는 클라이언트로부터의 기능을 대부분 호출합니다. 이전 단원에서 논의된 의사 시스템 저장 프로시저 또한 논리적으로는 SQL 관리자의 일부입니다. 일반적으로 TDS SQL 언어 메시지로 들어오는 SQL 문은 컴파일할 때 직접 처리합니다. SQL Server 7.0에서는 이전 버전에서만큼 빈번하지는 않지만 일반적으로 사용되는 방법입니다. ODS를 호출하여 TDS 결과 메시지의 형식을 지정하는 실행 엔진의 구성 요소에서 결과를 되돌립니다.

출력의 대부분은 그림의 실행쪽에서 제공하며 그 결과물은 실제로 표현 서비스에서 제공합니다. Expression Services 라이브러리는 데이터 변환, 평가 예측(필터링), 산술 계산 등의 작업을 하는 구성 요소입니다. 이 라이브러리는 또한 ODS 계층과 작업하여 그 출력 결과물을 TDS 메시지 형식으로 지정합니다.

또한 관계형 엔진에서 추가 서비스를 제공하는 두 가지 구성 요소에 대해 간단하게 설명합니다. 하나는 CREATE TABLE, CREATE VIEW 등과 같은 정의 문을 처리하는 카탈로그 서비스 구성 요소로 카탈로그 서비스는 의사 테이블을 유형화하여 시스템 테이블을 처리합니다. 카탈로그 서비스 구성 요소는 기본적으로 관계형 엔진 내에 위치하지만 실제로 구성 요소 작업의 1/3은 저장소 엔진 영역 내에서 수행되므로 이 구성 요소는 공유 구성 요소로 간주됩니다.

다른 하나는 파이버와 스레드를 처리하기 위한 SQL Server 자체의 내부 스케줄러인 UMS(User Mode Scheduler)입니다. 이 스케줄러에는 서버 구성에 따라 파이버나 스레드에 작업이 할당되는 방법을 예약하여 SQL Server에서 SMP 시스템의 여러 프로세서 간에 적절하게 로드 균형을 조정하는 매우 복잡한 내부 메커니즘이 있습니다. 또한 UMS는 SQL Server가 동시에 너무 많은 스레드를 실행하는 것을 방지합니다. 마지막으로 이 스케줄러에는 우리에게 친숙한 시스템 프로시저가 있습니다. 논리적으로 시스템 프로시저는 관계형 엔진의 일부입니다. 쉽게 sp_helptext를 사용하여 이러한 시스템 프로시저를 정의하는 Transact-SQL 코드를 조사할 수 있으므로 이 프로시저는 분명히 서버 코드가 아닙니다. 그러나 시스템 프로시저의 목적은 응용 프로그램 사용을 위한 더 높은 그리고 더 적절한 수준에서 시스템 테이블과 같은 기본 서버 기능을 보여 주는 데 있으므로 이 프로시저를 서버의 일부로 간주합니다. 응용 프로그램 개발자가 더 쉽게 사용할 수 있는 높은 수준의 시스템 프로시저를 인터페이스로 사용하면 릴리스마다 기본 수준 시스템 테이블이 변경되더라도 응용 프로그램이 계속 작동할 수 있습니다.

SQL 처리 시 클라이언트/서버 상호 작용

이제 클라이언트 응용 프로그램이 SQL Server와 상호 작용할 때 일어나는 일에 대해 알아봅니다. 다음은 ODBC 호출에 대한 예제입니다.

SQLExecDirect(hstmt, "SELECT * FROM parts where partid =7", SQL_NTS)

OLE-DB에도 이와 거의 동일한 호출이 있지만 처리 과정이 ODBC 호출과 동일하므로 OLE-DB의 경우는 설명하지 않습니다. ODBC 호출은 SQL 문을 가져온 다음 SQL Server로 보내 실행합니다.

이 쿼리에서는 특정 부품 ID를 갖는 행에 대해 parts 테이블의 모든 열을 선택합니다. 다음은 임시 SQL의 기본 예제입니다. SQL Server 7.0 이전의 릴리스에서는 저장 프로시저와 비교되는 임시 SQL의 두드러진 특징 중 하나로 쿼리 최적화 프로그램에서 생성된 계획이 캐시되지 않는다는 점을 들 수 있습니다. 쿼리를 입력하면 컴파일되고 실행된 다음 계획은 폐기됩니다. 이후에 논의할 내용이지만 SQL Server 7.0에는 실제로 임시 쿼리의 계획을 캐시할 수 있는 메커니즘이 있습니다.

이 문을 SQL Server로 보내기 전에 몇 가지 제기해야 할 질문이 있습니다. 클라이언트는 모두 일종의 커서 개념을 제공하므로 클라이언트 내부에서 프로그래머가 요청할 결과 집합이나 커서 종류에 대해 질문을 제기해야 합니다. 가장 빠른 유형은 이 문서에서기본 결과 집합이라고 하는 유형입니다. 이 커서를 firehose 커서라고 하는데 전혀 커서로 간주되지 않는 경우도 있습니다. SQL 요청이 서버로 보내지면 서버는 다시 클라이언트로 결과를 보내기 시작하며 클라이언트에서 전체 집합을 모두 소비할 때까지 계속 보냅니다. 이는 거대한 소방 호스에서 클라이언트로 데이터를 뿜는 것에 비유할 수 있습니다.

클라이언트에서 이를 기본 결과 집합으로 결정하면 다음 작업으로 매개 변수 표식이 있는지 확인합니다. ODBC 및 OLE-DB에서 이 SQLExecDirect 호출을 사용할 때의 옵션 중 하나로 WHERE 절의 7과 같이 특정 값을 제공하는 대신 다음 예제처럼 상수를 물음표로 대체하여 매개 변수 표식을 전달할 수 있습니다.

SQLExecDirect(hstmt, "SELECT * FROM parts where partid =?", SQL_NTS)

별도로 매개 변수의 실제 값을 제공해야 한다는 점에 유의하십시오.

클라이언트는 SQL 문에 매개 변수 표식이 있는지, 특정 값이 있는지, 아니면 임시 매개 변수화되지 않은 SQL인지 인식하고 있어야 합니다. 이러한 사항은 클라이언트가 이 문에 내부적으로 수행하는 작업에 영향을 주며 실제로 SQL Server에 메시지로 보내지는 내용을 결정합니다. 이 경우에는 물음표가 없으므로 클라이언트가 단순히 이 요청을 SQL 언어 TDS 메시지로 보낸 다음 firehose 의 끝에서 그 결과를 받으려 한다는 것을 분명히 알 수 있습니다. 그런 다음 클라이언트는 응용 프로그램의 매개 변수에 따라 그 결과를 응용 프로그램에 반환할 수 있습니다. ODBC나 OLE DB API를 통한 요청의 측면에서 클라이언트의 내부 처리 선택 사항은 약간 모호할 수도 있습니다. 예를 들어, 응용 프로그램에서는 기본 결과 집합을 직접 요청하지 않습니다. 대신 ODBC에서 읽기 전용 커서와 앞으로 가기 전용 커서를 요청하면 클라이언트 내부에 관한 한 기본 결과 집합인 firehose 커서로 정의되는 행이 한 번에 하나씩 제공됩니다.

firehose 커서에는 기본적으로 한 가지 문제점이 있습니다. 모든 행을 다 소모한 후에야 클라이언트가 다른 SQL 문을 서버로 보낼 수 있습니다. 결과 집합에는 많은 수의 행이 있으므로 일부 응용 프로그램은 firehose 커서와 잘 작동하지 않습니다. 이후에 논의할 빠른 앞으로 가기 전용 커서는 특히 이 문제를 해결하기 위해 만들어진 SQL Server 7.0의 새로운 기능입니다.

SQL Server 7.0 이전 버전에서는 상수 대신 매개 변수 표식을 사용했는지 여부와 상관 없이 거의 동일한 방법으로 SQLExecDirect 호출이 처리되었습니다. 매개 변수 표식을 지정하면 클라이언트는 실제로 다른 호출(이 단원 시작 부분 예제에서의 값 "7")을 통해 사용자가 제공한 값을 취한 다음 물음표가 있는 위치에 삽입합니다. 그런 다음 다른 값으로 대체된 새 문이 임시 SQL 문으로 보내집니다. 서버에서 매개 변수화된 SQL을 사용함으로써 얻을 수 있는 이점은 없었습니다.

그러나 SQL Server 7.0에서 SQLExecDirect에 매개 변수 표식을 사용하면 SQL Server로 보내진 TDS 메시지는 SQL 언어 메시지가 아닙니다. 대신 sp_executesql 프로시저를 사용하여 서버로 보내지므로 TDS 프로토콜과 관련된 한 이는 RPC입니다. 클라이언트에서는 결과가 기본적으로 같고 클라이언트는 firehose 데이터를 다시 받습니다.

firehose 데이터를 다시 받지 않으려면 블록 커서나 스크롤 가능한 커서를 사용합니다. 이 경우 흐름이 복잡해집니다. SQL 텍스트를 통과하는 sp_cursoropen 진입점(의사 저장 프로시저 중 하나)으로 호출이 이루어집니다. sp_cursoropen은 SQL을 조작하여 스크롤 가능하게 만드는 논리를 추가하여 잠재적으로 일부 결과를 임시 테이블로 리디렉션한 다음 커서가 이제 열려 있음을 나타내는 커서에 핸들로 응답합니다. 프로그래머가 제어할 수 없는 영역에서 클라이언트는 sp_cursorfetch를 호출하고 사용자 응용 프로그램으로 반환된 하나 이상의 행을 클라이언트로 보냅니다. 클라이언트는 또한 sp_cursor를 사용하여 커서의 위치를 다시 지정하거나 특정 통계값을 변경합니다. 사용자가 커서 처리 작업을 마치면 클라이언트는 sp_cursorclose를 호출합니다.

이제 클라이언트에 하나의 행을 반환하는 단순한 경우를 검토해 봅니다. 기본 결과 집합의 경우에는 클라이언트에서 서버로, 다시 서버에서 클라이언트로 한 번의 메시지 라운드트립이 있습니다. 서버로 향하는 SQL 메시지 또는 sp_executesql이 있으며 그런 다음 결과가 다시 돌아옵니다. 동일한 하나의 행에 대해 firehose 커서가 아닌 경우 SQL Server에서 기존과 같은 상황이 발생합니다. 열기 작업을 하기 위한 라운드트립, 페치(fetch) 작업을 하기 위한 라운드트립, 닫기 작업을 하기 위한 라운드트립이 있습니다. 이 프로세스는 기본 결과 집합에서 사용하는 메시지의 세 배를 사용합니다. SQL Server 7.0에는 동일한 커서 인프라를 사용하는 빠른 앞으로 가기 전용 커서가 있습니다. 이 커서를 사용하면 추가 SQL 메시지를 보내기 전에 모든 결과 행을 처리할 필요가 없다는 점에서 firehose와 다릅니다. 따라서 다섯 개의 행을 가져왔는데 데이터가 남아 있는 경우 서버로 업데이트를 보낼 수 있습니다.

빠른 앞으로 가기 전용 커서는 서버에서 일반 커서보다 빠르고 두 개의 추가 옵션을 지정할 수 있도록 합니다. 하나는 autofetch이고 하나는 autoclose입니다. autofetch는 열기에 대한 응답 메시지의 일부로 첫 번째 행 집합을 반환합니다. autoclose는 마지막 행이 읽혀진 후 자동으로 커서를 닫습니다. 이 커서는 앞으로 가기 전용 및 읽기 전용이므로 뒤로 스크롤할 수 없습니다. SQL Server는 커서가 닫혔음을 알리는 이 마지막 데이터 집합과 함께 메시지를 전달합니다. 빠른 앞으로 가기 전용 커서를 사용하면 적은 수의 행에 대한 통신을 동일한 하나의 라운드트립으로 할 수 있습니다. 행 수가 많으면 최소한 각 행 블록에 대해 추가 비용을 지불하기만 하면 됩니다. 빠른 앞으로 가기 전용 커서를 사용하면 커서 처리가 기본 결과 집합의 처리와 상당히 비슷하게 됩니다.

그림 4에서는 SQLExecDirect 모델의 흐름을 보여 줍니다.

사용자 삽입 이미지

그림 4. 클라이언트/서버 상호 작용

준비/실행 모델

ODBC에서 SQLExecDirect로 호출하는 실행 직접 모델 이외에 ODBC와 OLE-DB에서 볼 수 있는 실행 모델에는 준비/실행 모델이 있습니다. SQL이 실행되도록 정의하는 작업은 실제 SQL 실행과는 별도의 단계에서 이루어집니다. 다음 예제에서는 ODBC에서의 이러한 작업을 보여 줍니다.

SQLPrepare(hstmt, "SELECT * FROM parts where partid =?", SQL_NTS)SQLExecute(hstmt)

준비/실행은 SQL Server 7.0 이전의 SQL Server에 대한 원시 모델이 아닙니다. 7.0에는 원시 인터페이스를 제공하는 두 개의 의사 시스템 저장 프로시저가 있습니다. 준비 호출에 대해서는 다시 커서의 종류가 무엇인지 살펴본 다음 sp_prepare와 sp_cursorprepare 중 하나를 호출합니다. 준비/실행은 SQL이나 저장 프로시저의 컴파일 부분을 처리하지만 실제로 계획을 실행하지는 않습니다. 대신 의사 시스템 저장 프로시저에서 계획에 핸들을 반환합니다. 이제 예를 들어, 다시 컴파일할 필요 없이 응용 프로그램에서는 서로 다른 매개 변수 값을 전달하면서 반복적으로 SQL을 재실행할 수 있습니다.

SQL Server 6.5에는 원시 인터페이스가 없으므로 준비와 실행의 두 단계가 열거되어야 했으며 다음 두 가지 방법 중 하나로 이러한 작업이 이루어집니다. 첫 번째 방법에서는 준비 단계가 일어나지 않습니다. 메타데이터를 반한하기 위한 부분적인 실행만 있으므로(해당 설정 옵션 있음) SQL Server에서는 결과의 형식에 대한 설명을 다시 응용 프로그램으로 보낼 수 있습니다. 두 번째 방법에서는 SQL Server에서 실제로 개별 사용자에게는 개인적인 임시 저장 프로시저를 만들므로 계획을 공유하지 않습니다. 두 번째 방법을 사용하면 tempdb 데이터베이스가 모두 채워질 수 있으므로 대부분의 응용 프로그램 개발자는 ODBC 구성 대화 상자에서 해당 확인란 선택을 해제함으로써 두 번째 방법을 사용하는 옵션을 해제합니다.

SQL Server 7.0에서 준비/실행 메서드는 SQL Server의 원시 기능으로 SQL 문이 준비된 다음 실행됩니다. 기본 결과 집합의 경우 응용 프로그램 프로그래머가 준비 작업에서 제공된 핸들로 sp_execute를 호출한 다음 문이 실행됩니다. 커서의 경우 다른 커서 처리와 동일하게 보이며 커서가 빠른 앞으로 가기 전용 커서일 때에는 autofetch와 autoclose를 허용하는 등의 동일한 특징이 있습니다.

그림 5에서는 준비/실행 작업의 흐름을 보여 줍니다.

사용자 삽입 이미지

그림 5. 준비/실행 모델

저장 프로시저 호출

일반적으로 ODBC 표준 CALL 구문을 사용하여 프로시저를 호출하는 SQL Server로 SQL 문을 보냄으로써 ODBC와 OLE-DB로부터 저장 프로시저를 호출합니다. 이는 다음과 비슷합니다.

SQLExecDirect(hstm, "{call addorder(?)}", SQL_NTS)

기본 결과 집합의 경우 RPC 메시지의 원래 의도대로 이 흐름은 단순합니다. 클라이언트에서 서버로 RPC 메시지를 보낸 다음 프로시저로부터의 결과를 다시 받습니다. 커서인 경우에는 약간 더 복잡합니다. 클라이언트에서 다른 커서와 마찬가지로 sp_cursoropen을 호출합니다. Sp_cursoropen에는 저장 프로시저에 하나의 SELECT 문만 있는지 검사하기 위해 내장된 논리가 몇 가지 있습니다. 하나의 SELECT 문만 있으면 해당 SELECT에서 커서가 열립니다. 프로시저에 있는 유일한 SELECT 문이 아니면 클라이언트에서는 "대신 열었지만 firehose로 결과를 다시 스트림할 것이므로 이 결과를 사용자에게 제공할 수 있습니다."라고 알리는 표시기와 함께 메시지를 되돌려 받습니다.

그림 6에서는 저장 프로시저 처리에 대한 실행 흐름을 보여 줍니다.

사용자 삽입 이미지

그림 6. 저장 프로시저 호출

SQL 관리자

이전에 살펴보았던 SQL 관리자는 여러 서버 작업에 있어 원동력이 되고 서버의 핵심에 있습니다. SQL 관리자는 모든 요청을 처리하여 저장 프로시저를 실행합니다. SQL 관리자는 프로시저 캐시를 관리하고 의사 시스템 저장 프로시저를 포함하며 임시 쿼리의 자동 매개 변수화 작업에 참여합니다. 이에 대해서는 간략하게 설명합니다. SQL 6.5나 그 이전 버전에 대해 이 글과 유사한 글을 읽은 적이 있더라도 SQL 관리자에 대한 설명은 없는 대신 SQL 관리자가 하는 작업의 일부를 수행하는 다른 구성 요소에 대한 설명은 있었을 것입니다. 그러나 SQL Server 7.0에서는 이러한 모든 구성 요소가 시스템에서 쿼리 처리를 추진하는 하나의 구성 요소로 통합되었습니다.

일반적으로 SQL Server에 작업을 요청하는 것과 마찬가지로 RPC 메시지를 사용하여 SQL 관리자를 호출합니다. 그러나 SQL 메시지를 통해 SQL 언어 문이 들어온 다음 엔진의 컴파일 쪽으로 들어가게 되면 SQL 관리자 또한 작업에 개입하게 됩니다. EXEC가 실제로 SQL 관리자를 호출하므로 프로시저나 배치에 EXEC 문이 있을 때 SQL 관리자가 개입될 수 있습니다. SQL 문이 이후에 논의할 항목인 자동 매개 변수화 템플릿을 전달하면 SQL 관리자가 호출되어 쿼리를 매개 변수화합니다. 캐시에 임시 쿼리를 배치해야 할 때에도 SQL 관리자가 호출됩니다.

컴파일 및 실행

이제 SQL Server 내에서 컴파일과 실행이 일어나는 방법에 대해 살펴봅니다. 컴파일과 실행은 SQL Server 내에서 별개로 일어나는 두 단계입니다. SQL Server가 쿼리를 컴파일하는 시간과 쿼리가 실행되는 시간 간의 간격은 몇 밀리초로 매우 작을 수도 있고 몇 초, 몇 분, 몇 시간, 며칠이 될 수도 있습니다. 최적화를 포함한 컴파일에서 우리가 컴파일의 일부로 사용할 수 있는 지식의 종류를 구분해야 합니다. 컴파일할 때 해당되는 사항이 실행할 때에도 해당되는 것은 아니므로 컴파일과 실행을 서로 다른 별도의 작업으로 생각해야 합니다. 임시 SQL 문으로 쿼리를 보낸 다음 즉시 실행하는 경우에도 컴파일과 실행은 별개의 작업입니다.

SQL Server에서 쿼리를 처리할 준비가 되면 SQL 관리자가 캐시에서 이 쿼리를 조회합니다. 캐시에 쿼리가 없으면 컴파일된 것입니다. 컴파일 프로세스에서는 몇 가지 작업을 수행합니다. 첫째로 구문 분석과 정규화 작업을 합니다. 구문 분석을 하면 SQL 문을 분석한 다음 컴퓨터에서 더 쉽게 처리할 수 있는 데이터 구조로 바꾸고 문이 올바른지 검사합니다. 테이블과 열 이름이 유효한지 여부를 확인하는 등의 작업은 분석에 포함되지 않고 정규화 작업을 하는 동안 수행됩니다. 기본적으로 정규화는 SQL 내에서 실제 특징이 무엇인지를 참조하는 항목이 데이터베이스에 있는지 확인한 다음 요청하는 의미론이 타당한지 여부를 확인하는 작업을 하도록 만들어졌습니다. 예를 들어, 테이블을 실행하려는 것은 의미론적으로 타당하지 않습니다.

그 다음 단계는 Transact-SQL 코드를 컴파일하는 것입니다. 일반 사용자는 Transact-SQL과 SQL 자체 간의 차이점을 분명하게 구별하는 것이 모호하고 Microsoft의 개발자들 또한 Transact-SQL과 SQL을 동일하게 사용합니다. 그러나 이 두 항목 사이에는 중요한 차이점이 있습니다. SQL은 INSERT, UPDATE, DELETE, SELECT와 같은 DML 문입니다. SQL Server에도 이러한 DML 문을 포함하는 Transact-SQL 또는 TSQL이라고 하는 언어가 있어 IF 문, WHILE 문, 지역 변수 선언 등의 프로시저 구문을 제공합니다. 서버 내에서 이러한 구문들은 서로 완전히 다르게 취급되고 프로시저 작업을 수행할 수 있는 엔진에서 TSQL의 프로시저 논리를 컴파일합니다.

SQL 문 자체는 기본 쿼리 최적화 프로그램에서 컴파일됩니다. 최적화 프로그램은 집합 기준 SQL 문의 비프로시저 요청을 프로시저로 변환하고 이 프로시저가 효율적으로 실행되어 원하는 결과를 반환할 수 있도록 합니다. 이후부터는 별도로 언급하지 않는 한 컴파일은 TSQL의 컴파일과 SQL 문의 최적화 둘 다를 의미합니다.

앞에서 컴파일과 실행은 쿼리 처리에 있어 별도의 두 단계임을 언급한 적이 있습니다. 따라서 최적화 프로그램에서 하는 작업 중 하나는 매우 안정적인 상태를 바탕으로 최적화를 수행하는 것입니다. SQL Server에서 충족되는 특정 기준에 따라 문을 다시 컴파일할 수도 있으므로 이 안정적 상태는 영구히 안정적인 것을 의미하는 것이 아니라 일정한 유입 상태에 있지 않는 상태를 의미합니다. 최적화 프로그램에서 매우 많이 그리고 아주 자주 변경되는 정보를 사용하면 동시 처리의 수나 잠금 수가 고정되므로 쿼리를 계속해서 다시 컴파일해야 되고 컴파일 진행이 느려집니다. 예를 들어, 1/100초로 실행되는 SQL 문을 컴파일하는 데 1/2초가 소요될 수 있습니다. 일단 SQL Server에서 이 문을 컴파일한 다음에는 SQL Server에서 모든 문마다 컴파일하게 하는 것보다는 몇 번이든지 직접 컴파일하는 것이 좋습니다.

마지막으로 컴파일 단계에서는 프로시저 캐시로 삽입되는 쿼리 계획을 만듭니다. 비용이 저렴한 임시의 SQL 계획은 실제로 캐시로 들어가지 않지만 여기서는 이에 대해 자세하게 설명하지 않습니다. 다시 사용하지 않을 내용으로 캐시를 넘치게 할 필요가 없으며 임시 SQL은 그 중 많은 부분을 차지하고 있고 해당 계획을 다시 사용하지 않을 가능성이 가장 높은 쿼리 유형입니다. 해당 문을 1/100초 미만으로 저렴하게 컴파일할 수 있으면 캐시에 계획을 저장하여 다시 사용하지 않을 계획으로 캐시를 채울 필요가 없습니다.

캐시에 계획이 삽입된 후 SQL 관리자는 계획을 실행하고 변경된 내용이 있는지와 계획을 다시 컴파일해야 하는지를 확인하는 작업의 관점에서 해당 논리를 다시 살펴봅니다. 컴파일과 실행을 분리하는 시간 간격은 마이크로 초에 불과하지만 그 동안 누군가가 중요 테이블에 색인을 추가하는 DDL(데이터 정의 언어) 문을 실행했을 수도 있습니다. 이러한 일이 일어날 가능성은 거의 없지만 일어날 수도 있으므로 SQL Server에서는 이를 감안해야 합니다. SQL Server가 저장된 계획을 다시 컴파일하게 하는 몇 가지 요인이 있습니다. 인덱스를 추가하거나 제거하는 등 메타데이터가 변경되는 것이 가장 주요한 원인입니다. 이때 서버에서는 사용된 계획이 현재 인덱스 상태를 반영하도록 해야 합니다.

컴파일을 다시 하는 또 다른 원인은 통계 변경에 있습니다. SQL Server에서는 처리하는 데이터에 약간의 히스토그램 정보를 유지합니다. 데이터 분산이 많이 변경된 경우 효율적으로 실행하려면 다른 쿼리 계획이 필요합니다. SQL Server는 데이터가 테이블에 삽입되거나 삭제되는 빈도를 추적하여 변경 횟수가 테이블 크기를 기준으로 변경되는 특정 임계값을 초과하면 다시 되돌아가 새 분산 정보를 바탕으로 계획을 다시 컴파일합니다.

그림 7에서는 컴파일과 실행의 흐름을 보여 줍니다.

사용자 삽입 이미지

그림 7. 컴파일 및 실행

실제 매개 변수 값의 변경 및 사용 가능한 메모리 양이나 이미 캐시에 있는 필요 데이터의 양 등 환경이 변함에 따라서는 계획이 다시 컴파일되지않는다는 점에 유의하십시오.

실행은 직선적 작업입니다. 따라서 "한 행 삽입"이나 고유 인덱스가 있는 테이블에서 하나를 선택하는 등의 매우 간단한 쿼리를 실행할 때는 그 처리 과정이 매우 단순합니다. 그러나 대부분의 쿼리를 효과적으로 실행하거나 최소한 메모리의 이점을 취하려면 많은 메모리가 필요합니다. SQL Server 6.5에서는 쿼리에서 사용할 수 있는 메모리 양을 0.5MB나 1MB로 제한함으로써 메모리 사용을 제어했습니다. SQL Server 6.5에는 쿼리에서 사용하는 메모리 양을 제어하는 구성 매개 변수인sort pages가 있었습니다. 이름을 통해 알 수 있듯이 이 매개 변수는 기본적으로 많은 양의 메모리를 사용해야 효과적으로 실행될 수 있는 작업을 정렬합니다. 그러나 많은 메모리를 필요로 하는 작업이라도 SQL Server 6.5에서는 특정 작업에 1MB 이상을 사용할 수 없었습니다. 메모리가 1GB나 2GB인 컴퓨터에서 작업하고 행이 수백만 개라 하더라도 마찬가지로 1MB 이상을 사용할 수 없었습니다. 따라서 복잡한 쿼리에는 효과적이지 않으므로 이에 따라 SQL Server 개발자들은 개별 쿼리에 많은 양의 메모리를 사용하도록 하는 기능을 SQL Server 7.0에 추가했습니다.

많은 메모리를 사용할 수 있게 됨에 따라 또 다른 문제가 발생하게 되었습니다. 일단 쿼리에서 많은 양의 메모리를 사용하기 시작하면 이 메모리를 사용할 여러 쿼리 간에 메모리를 조정할 방법을 결정해야 합니다. SQL Server에서는 이러한 문제를 다음과 같이 처리합니다. 쿼리 계획이 최적화되면 최적화 프로그램은 해당 쿼리의 메모리 사용에 대해 두 가지 정보를 결정합니다. 첫째, 쿼리를 효과적으로 실행하기 위해 필요한 최소 메모리 양을 선택하면 이 값이 쿼리 계획에 저장되고 최적화 프로그램에서 쿼리가 제대로 실행되기 위해 필요한 최대 메모리 양을 결정합니다. 예를 들어, 정렬하고 있는 전체 테이블이 100MB에 적합할 경우에는 2GB의 메모리를 제공할 필요가 없습니다. 실제로 필요한 메모리는 100MB이므로 이 값이 쿼리 계획에 저장되는 메모리 최대 사용량이 됩니다.

SQL Server에서 계획을 실행하려고 하면 이 계획이 메모리 부여 스케줄러라고 하는 루틴으로 전달됩니다. 부여 스케줄러는 몇 가지 흥미로운 작업을 수행합니다. 첫째, 부여 스케줄러에서 검토하고 있는 쿼리에 계획의 일부로 정렬이나 해시 작업이 없으면 SQL Server는 이 쿼리에서 많은 메모리를 사용하지 않는다는 것을 인지합니다. 이 경우 메모리 부여 스케줄러는 대기하지 않습니다. 계획은 즉시 실행될 수 있고 따라서 일반 트랜잭션 처리 요청에서는 이 메커니즘이 철저히 무시됩니다. 또한 메모리 부여 스케줄러에는 여러 크기의 요청을 처리하는 데 사용할 수 있는 몇몇 대기열이 있습니다. 메모리 스케줄러는 크기가 작은 요청에 우선 순위를 줍니다. 예를 들어, "최상위 10개 선택"이라는 쿼리가 있고 20개의 행만 정렬하려는 경우 이 쿼리는 메모리 부여 스케줄러를 통과해야 하지만 신속하게 스케줄러에서 나온 다음 빨리 실행되도록 일정이 만들어집니다. 서버에서는 이러한 여러 개의 작은 쿼리를 병렬로 또는 동시에 실행하려고 하기 때문입니다.

여러 개의 대규모 쿼리가 있는데 이 중 몇 개만 실행하려면 쿼리에서 필요한 메모리 양을 늘리도록 합니다. SQL Server에서는 이 값을 4X(시스템의 CPU 수)가 되도록 계산합니다. 가능하면 SQL Server에서는 각 쿼리에 효과적인 최소 메모리 크기가 주어지면서 이 수에 해당하는 쿼리를 동시에 실행합니다. 그러고도 남은 메모리가 있으면 일부 쿼리에서는 효과적인 최대 메모리를 사용할 수 있습니다. SQL Server에서는 쿼리에 사용할 수 있는 메모리 양을 최대화하는 동시에 시스템에서 많은 쿼리를 실행합니다.

효과적인 최대 메모리 사용 기능을 야간 배치 사이클 등의 작업에서 아주 유용하게 사용할 수 있습니다. 매우 큰 보고서를 생성하거나 인덱스를 다시 만들어야 하는 경우가 있습니다. 이러한 쿼리는 아주 많은 메모리를 사용할 수 있으므로 이 메커니즘이 필요에 따라 동적으로 조정합니다. 따라서 대기열에서 오래 대기하지 않는 경우 메모리 부여 스케줄러가 매우 자주 쿼리에 요청된 최대 메모리를 제공합니다. 주간에 복잡한 처리가 필요한 작업을 하는 경우 더 많은 쿼리를 동시에 실행하게 됩니다. 이러한 쿼리는 효과적으로 실행될 수 있도록 최소 메모리 할당만큼을 가져가지만 메모리는 공유 리소스보다 많이 사용하게 됩니다.

일단 스케줄러에서 요청에 메모리를 할당할 수 있음을 알리면 계획이 "열립니다". 즉, 실제 실행이 시작됩니다. 여기서부터 계획이 실행되기 시작하여 완료됩니다. 쿼리에서 기본 결과 집합 모델을 사용하면 모든 행이 만들어질 때까지 계획이 실행되고 모두 클라이언트로 다시 보내집니다. 쿼리에서 커서 모델을 사용하면 처리가 약간 달라집니다. 이 경우 전체 행이 아닌 하나의 행 블록에 대해 각 클라이언트 요청이 이루어집니다. 각 결과 집합이 클라이언트로 다시 보내지면 SQL Server는 클라이언트에서 다음 집합을 요청할 때까지 대기해야 하고 대기하는 동안 전체 계획이정지됩니다. 즉, 잠금 일부는 해제되고 일부 리소스는 포기되며 일부 위치 정보는 저장됩니다. 이 정보를 통해 SQL Server는 다음 행 집합이 요청될 때 원래 위치로 돌아가서 실행을 계속합니다.

프로시저 캐시

SQL Server의 프로시저 캐시 개념에 대해 몇 번 언급한 적이 있습니다. SQL Server 7.0의 프로시저 캐시는 이전 버전과는 완전히 다르다는 점을 이해하는 것은 아주 중요합니다. 이전 릴리스에서는 프로시저 캐시의 크기를 제어하는 효과적인 구성 값이 두 가지 있었습니다. 한 값은 SQL Server의 사용할 수 있는 총 메모리의 고정 크기를 지정하고 나머지 값은 고정된 값을 채운 다음 쿼리 계획을 저장하는 데만 사용될 메모리의 백분율입니다. 이전 버전에서도 임시 SQL 문에 대한 쿼리 계획은 캐시에 저장되지 않고 저장 프로시저에 대한 계획만 저장되었습니다. In SQL Server 7.0에서 메모리의 총 크기는 기본적으로 동적이며 쿼리 계획에 사용되는 공간 또한 매우 유동적입니다.

쿼리를 처리할 때 SQL Server 7.0에서 가장 먼저 물어보는 것 중 하나가 쿼리가 임시 쿼리인동시에컴파일하기에 저렴한지 여부입니다. 그렇다면 SQL Server에서는 쿼리를 캐시하지 않습니다. 비용이 더 많이 드는 계획이나 데이터 페이지의 메모리를 차지하기 보다는 나중에 계획을 다시 컴파일하는 것이 더 저렴하기 때문입니다. 쿼리가 임시 쿼리가 아니거나 컴파일 비용이 저렴하지 않으면 SQL Server에서는 버퍼 캐시에서 약간의 메모리를 가져온 다음 이 메모리에 계획을 저장합니다. SQL Server 7.0에서는 서버에서 필요로 하는 메모리의 99%가 버퍼 캐시에 있으므로 메모리는 버퍼 캐시로부터 나옵니다. SQL Server에서는 대량의 메모리를 운영 체제에서 직접 할당하는 경우도 가끔 있지만 보기 드뭅니다. 그 외의 경우는 관리가 중앙 집중화되어 있습니다.

쿼리를 컴파일함으로써 실제로 계획을 만드는 데 소요되는 비용을 반영하는 비용 요소와 함께 계획이 캐시에 저장됩니다. 임시 계획일 경우 SQL Server에서는 비용을 0으로 설정합니다. 즉, 이 계획은 프로시저 캐시에서 즉시 빠져 나갑니다. 임시 SQL의 경우 다시 사용할 수는 있지만 그 가능성은 매우 낮습니다. 시스템에 메모리 압력이 있으면 임시 문에 대한 계획이 먼저 나가도록 합니다. 따라서 임시 쿼리에 대한 계획은 캐시 밖에서 부팅 가능한 것부터 나가기 시작합니다. 쿼리가 임시 쿼리가 아니면 SQL Server에서는 쿼리를 컴파일하기 위한 실제 비용으로 비용을 설정합니다. 비용 단위는 디스크 I/O이고 디스크에서 하나의 데이터 페이지를 읽으면 그 비용은 1I/O입니다. 이 계획이 만들어지면 디스크에서 통계 및 쿼리 자체의 텍스트를 포함한 정보를 읽습니다. SQL은 추가 처리를 하고 이 처리 작업은 I/O 비용에 맞게 정규화됩니다. 이제 I/O 비용에 대해 프로시저 작성 비용이 부과됩니다. 데이터를 디스크 캐싱할 양에 비해 실제로 저장 프로시저로 들어갈 캐시의 양이나 쿼리 계획 종류를 관리하는 기능에서 비용이 많이 듭니다. 비용이 계산되면 계획이 캐시에 저장됩니다.

그림 8에서는 계획에 대한 비용이 계산되고 계획이 캐시에 저장되는 흐름을 보여 줍니다.

사용자 삽입 이미지

그림 8. 캐시에 계획 삽입

계획을 다시 사용할 수 있는 다른 쿼리가 있으면 SQL Server에서는 다시 계획의 유형을 확인합니다. 임시 계획이면 SQL Server에서는 비용을 1씩 증분합니다. 따라서 임의 계획을 정말로 다시 사용하는 경우 비용 요소가 증가함에 따라 캐시에 좀 더 오래 남아있게 됩니다. 계획이 자주 다시 사용되면 실제 작성 비용에 이를 때까지 비용이 계속해서 1씩 증가합니다. 이렇게 되면 비용이 가장 높게 설정된 것입니다. 그러나 여러 번 다시 사용되므로 같은 사용자나 다른 사용자가 계속해서 똑같은 SQL 텍스트를 제출하면 이 계획은 캐시에 남아있게 됩니다.

쿼리가 임시 쿼리가 아니면, 즉 저장 프로시저, 매개 변수화된 쿼리 또는 자동 매개 변수화된 쿼리이면 계획이 다시 사용될 때마다 비용이 다시 작성 비용으로 설정됩니다. 계획은 다시 사용되는 한 캐시에 남습니다. 얼마 동안 사용되지 않더라도 처음부터 다시 만들 때 소요되는 비용이 많으면 꽤 오랫동안 캐시에 남을 수도 있습니다.

그림 9에서는 캐시에서 계획을 가져오고 비용을 조정하는 흐름을 보여 줍니다.

사용자 삽입 이미지

그림 9. 캐시에서 계획 가져오기

lazywriter는 계획을 보관하고 필요할 때 캐시에서 제거하는 메커니즘입니다. 실제로 lazywriter는 저장소 엔진의 일부이지만 쿼리 처리 메커니즘에서 아주 중요한 역할을 하므로 여기서 논의합니다. SQL Server 7.0에서는 일반 버퍼 캐시에 계획이 저장되므로 lazywriter는 쿼리 계획에서 사용하는 메모리를 관리하기 위한 메커니즘과 동일한 메커니즘을 사용하여 페이지를 관리합니다. lazywriter는 시스템의 모든 버퍼 헤더를 조사합니다. 시스템에 메모리 압력이 거의 없으면 lazywriter는 매우 느리게 실행되는 것처럼 보입니다. 메모리 압력이 증가하면 lazywriter는 더 빈번히 실행되기 시작합니다. 실행되면서 lazywriter는 버퍼 헤더와 해당 버퍼의 페이지에 대한 현재 비용을 검토합니다. 비용이 0이면 lazywriter가 마지막 실행된 후 페이지가 사용되지 않았음을 나타내고 이 경우 lazywriter는 이 페이지를 해제하여 페이지 I/O나 다른 계획에 사용할 수 있도록 시스템의 메모리를 확보합니다. 또한 버퍼에 프로시저 계획이 있으면 lazywriter에서는 정리 작업을 하도록 SQL 관리자를 호출합니다. 마지막으로 다시 사용할 수 있도록 버퍼가 사용 가능 목록에 삽입됩니다.

버퍼와 관련된 비용이 0보다 크면 lazywriter에서 비용을 감소시키고 계속해서 다른 버퍼를 조사합니다. 그러면 이 비용은 실제로 어떤 항목이 다시 사용되지 않고 캐시에 남아 있는 경우의 lazywriter의 사이클 수를 나타냅니다. 개체가 저장 프로시저인 경우 SQL 관리자를 호출하는 단계를 제외하고 이 알고리즘은 캐시의 계획과 캐시의 데이터나 인덱스 페이지 간에 서로 다르지 않습니다. lazywriter는 프로시저가 되는 개체에 대해 알지 못하므로 이 알고리즘은 디스크 I/O에 대한 캐시 사용과 프로시저 계획에 대한 캐시 사용 간에 적절히 균형을 이룹니다.

컴파일 비용이 많이 드는 항목은 초기 비용이 많이 들기 때문에 오랫동안 사용되지 않더라도 캐시에 남아 있습니다. 자주 다시 사용되는 항목은 사용될 때마다 비용이 재설정되므로 캐시에 남게 되고 lazywriter에서는 이 값이 0이 되지 않습니다.

그림 10에서는 lazywriter의 캐시 처리 흐름을 보여 줍니다.

사용자 삽입 이미지

그림 10. lazywriter 캐시 처리 흐름

클라이언트의 SQL 처리

이제 SQL 문이 제출될 때 일어나는 처리 과정을 다시 한 번 살펴봅니다. 먼저 클라이언트에서 SQL Server로 RPC 이벤트가 들어가는 경우를 알아봅니다. SQL Server는 RPC 이벤트를 받아들이므로 이 이벤트가 일종의 매개 변수화된 SQL, 즉 준비/실행 모델이나 EXECUTESQL 중 하나라는 것을 인지하고 있습니다. 따라서 SQL Server에서는 이러한 SQL Server 텍스트를 식별할 캐시 키를 만들어야 합니다. 대신 SQL Server에서 실제 저장 프로시저를 처리하는 경우에는 자체 키를 만들 필요 없이 프로시저 이름을 사용하면 됩니다. RPC 호출을 통해 들어오는 SQL 텍스트인 경우 SQL 텍스트를 해싱하여 캐시 키가 만들어집니다. 또한 이 키는 ANSI 설정 등 특정 상태 정보를 반영해야 합니다. 같은 쿼리를 전달하더라도 모든 ANSI 설정이 ON으로 설정되어 있는 연결과 모든 ANSI 설정이 OFF로 설정되어 있는 연결에서는 같은 계획을 사용할 수 없습니다. 이 두 연결의 처리 과정은 서로 다릅니다. 예를 들어, 완전히 같은 SQL 텍스트를 실행하더라도 한 연결의 concat_null_yields_null 값이 ON으로 설정되어 있으면 concat_null_yields_null 값이 OFF로 설정된 연결에서 만들어진 결과와는 완전히 다른 결과가 만들어질 수 있습니다. 따라서 SQL Server에서는 계획의 복사본을 캐시에 여러 개, 사용 가능한 ANSI 설정의 각 조합에 대해 하나씩 유지합니다. 사용 가능한 옵션 집합은 키의 일부이며 이 키는 이러한 캐싱 메커니즘을 사용하여 데이터를 조회하는 핵심에 있으므로 SQL Server에서는 이 키를 만들어 캐시에서 조회할 때 사용합니다. 캐시에서 계획을 찾지 못하면 SQL Server에서는 이전에 설명한 대로 계획을 컴파일한 다음 해당 키와 함께 캐시에 저장합니다.

또한 SQL Server에서는 해당 명령이 준비 작업이어서 계획이 컴파일만 되고 실행되지는 않을 것인지 여부를 결정해야 합니다. 준비 작업이면 SQL Server에서는 나중에 클라이언트가 계획을 가져와서 실행할 때 사용할 수 있도록 핸들을 클라이언트로 다시 반환합니다. 준비 작업이 아니면 SQL Server에서는 캐시에서 원래 조회할 때 이 계획을 발견한 것처럼 계획을 가져와서 실행합니다.

준비/실행 모델은 캐시 관리에 복잡한 요소를 하나 추가합니다. 준비하면 나중에 계획을 실행할 때 사용할 수 있는 핸들을 제공하고 응용 프로그램에서는 정기적으로 계획을 실행하면서 이 핸들을 몇 시간 또는 며칠 동안 활성화 상태로 유지합니다. 캐시에 더 많은 활성 계획이나 데이터 페이지를 위한 공간을 만들어야 할 경우에는 이 핸들을 무효화할 수 없습니다. SQL Server에서 실제로 하는 일은 캐시에 계획을 삽입하는 것입니다. 그 외에 SQL Server는 SQL이 밀집된 공간에서 이루어지는 준비 작업에 참여하지 않도록 합니다. 메모리 압력이 있는 경우 계획에서 사용하는 공간에서 이전에 설명한 방법으로 여유 공간을 확보할 수 있지만 그래도 준비된 SQL 복사본이 남아 있습니다. 준비된 SQL을 실행하기 위해 클라이언트에서 작업을 시작하는데 캐시에 계획이 없으면 SQL Server가 텍스트를 가져와서 다시 컴파일한 다음 캐시에 삽입할 수 있습니다. 이런 방법으로 계획을 보유하기 위해 사용되는 캐시의 16KB 이상의 페이지를 다시 사용할 수 있으며 오랫동안 보유되는 이 공간은 다른 위치에 저장된 100바이트나 200바이트의 SQL 코드입니다.

또 다른 경우는 쿼리가 SQL 언어 이벤트로 들어올 때 클라이언트에서 받은 문을 처리하는 것입니다. 한 가지 경우를 제외하고는 첫 번째 흐름과 많이 다르지 않습니다. 이 경우 SQL Server에서는 자동 매개 변수화라고 하는 기술을 사용하려고 합니다. SQL 텍스트가 자동 매개 변수화 템플릿에 대해 일치됩니다. 자동 매개 변수화는 어려운 문제이므로 과거에 공유 SQL을 사용할 수 있었던 다른 데이터베이스 관리 제품에서는 일반적으로 이 옵션을 제공하지 않았습니다. 자동 매개 변수화에 있어 한 가지 문제점은 SQL Server에서 모든 쿼리를 자동으로 매개 변수화하면 일부 또는 대부분의 쿼리가 이후에 제출될 특정 값에 대해서는 그다지 적절하지 않은 계획을 갖게 된다는 점입니다. 프로그래머가 코드에 매개 변수 표식을 삽입한 경우에는 기대할 수 있는 값의 범위를 알고 있으므로 SQL Server에서 제공하는 계획을 기꺼이 받아들일 것입니다. 그러나 실제로 프로그래머가 특정 값을 제공했는데 SQL Server에서 이 값을 변경 가능한 매개 변수로 취급하면 한 값에 대해 작동하도록 생성된 계획이 뒤따르는 값에 대해서는 작동하지 않을 가능성이 있습니다. 저장 프로시저를 사용할 경우에는 프로그래머가 프로시저에 WITH RECOMPILE 옵션을 삽입하여 새 계획을 생성할 수 있습니다. 자동 매개 변수화를 사용할 경우에는 프로그래머가 각 새 값에 대해 새 계획을 개발해야 한다는 사실을 표시할 수 없습니다.

자동 매개 변수화와 관련된 부분에서 SQL Server는 매우 보수적으로 안전하게 자동 매개 변수화될 수 있는 쿼리 템플릿이 있으며 이 템플릿에 일치하는 쿼리에만 자동 매개 변수화가 적용됩니다. 예를 들어, 하나의 같음 연산자가 있고 조인은 없으며 열에 고유 인덱스가 있는 WHERE 절을 포함한 쿼리를 가정합니다. SQL Server에서는 하나 이상의 행이 반환되지 않을 것이며 계획에서 항상 이 고유 인덱스를 사용해야 한다는 점을 인지하고 있습니다. SQL Server에서는 검색을 고려하지 않을 것이며 실제 값은 어떤 방법으로든 계획을 변경하지 않습니다. 따라서 이러한 쿼리는 안전하게 자동 매개 변수화할 수 있습니다.

쿼리가 자동 매개 변수화 템플릿과 일치하면 SQL Server에서는 리터럴을 매개 변수 표식(예: @p1, @p2)으로 대체하며 이것을 sp_executesql 호출인 것처럼 서버로 보냅니다. SQL Server에서 해당 쿼리를 자동 매개 변수화하기에 안전하지 않다고 결정하면 클라이언트가 SQL Server에 임시 SQL로 리터럴 SQL 텍스트를 보냅니다.

그림 11에서는 클라이언트가 SQL Server로 요청을 보낼 때의 처리 흐름을 보여 줍니다.

사용자 삽입 이미지

그림 11. 클라이언트의 SQL 처리

컴파일

이제 컴파일과 최적화에 대해 좀 더 자세히 알아봅니다. 컴파일하는 동안 SQL Server에서는 문을 분석하고 이 문에 대한 내부 표현인 시퀀스 트리라는 것을 만듭니다. 시퀀스 트리는 SQL Server 6.5에서 SQL Server 7.0으로 넘어오면서 없어지지 않은 데이터 구조 중 하나입니다. 그런 다음 시퀀스 트리는 정규화됩니다. 정규화의 기본 기능은 바인딩입니다. 바인딩에는 테이블과 열이 있는지 확인하는 작업과 테이블과 열에 대한 메타데이터를 로드하는 작업이 있습니다. 필요한 암시적 변환에 대한 정보 또한 시퀀스 트리에 추가됩니다. 예를 들어, 쿼리가 숫자 값에 정수 10을 추가하려고 하면 SQL Server에서 트리에 암시적 변환을 삽입합니다. 정규화는 또한 뷰에 대한 참조를 뷰의 정의로 바꿉니다. 마지막으로 정규화는 몇 가지 구문을 기준으로 최적화를 수행합니다. 해당 문이 기본 SQL 문이면 SQL Server가 시퀀스 트리에서 해당 쿼리에 대한 정보를 추출한 다음 최적화 프로그램이 효율적으로 작동할 수 있도록 설정된 쿼리 그래프라고 하는 특수한 구조를 만듭니다. 그런 다음 쿼리 그래프가 최적화되고 계획이 만들어집니다.

그림 12에서는 컴파일 프로세스의 흐름을 보여 줍니다.

사용자 삽입 이미지

그림 12. 컴파일

최적화

SQL Server 최적화 프로그램은 별개의 부분으로 이루어져 있습니다. 첫 번째 부분은 일반 계획 최적화라고 하는 비용 외 기준 최적화 프로그램입니다. 일반 계획 최적화의 전체 개념은 SQL 문에 단 하나의 실행 가능한 계획이 있을 경우 비용 기준 최적화 실행에 비용이 많이 든다는 것입니다. 일반적으로 하나의 VALUES 절이 있는 하나의 INSERT 문으로 이루어진 쿼리가 있습니다. 이 쿼리에는 가능한 계획이 하나뿐입니다. 또 다른 예는 SELECT 문으로 이 문에서는 모든 열이 고유 포함 인덱스에 있고 이러한 열 집합을 갖는 다른 인덱스는 없는 경우입니다. 이와 같은 두 가지 경우 SQL Server에서는 단순히 계획을 생성할 뿐 여러 계획을 조사하여 더 나은 계획이 있는지 조사하려고 하지 않습니다. 일반 계획 최적화 프로그램은 매우 명백하고 별로 비용이 들지 않는 계획들을 찾습니다. 따라서 가장 단순한 쿼리가 최적화 프로세스에서 빨리 제거되므로 최적화 프로그램에서는 적절한 계획을 검색할 때 많은 시간이 들지 않습니다. SQL Server에서 해시 조인, 병합 조인, 인덱스 교점을 처리 기술 목록에 추가할 때 SQL Server 7.0의 잠재적 계획 수가 급증하므로 이러한 방법이 적절합니다.

일반 계획 최적화 프로그램에서 계획을 찾지 못하면 SQL Server가 단순화로 알려진 최적화의 다음 부분으로 들어갑니다. 단순화는 쿼리 자체의 문장론적 변형으로 재정렬될 수 있는 대체 속성 및 작업을 찾습니다. SQL Server에서는 지속적인 접기 및 비용을 검토하거나 더 효율적인 쿼리를 만들 수 있는 인덱스를 분석할 필요가 없는 다른 작업을 수행합니다. 그런 다음 SQL Server는 인덱스와 열로 통계 정보를 로드하고 비용 기준 최적화 프로그램인 마지막 주요 최적화 부분으로 들어갑니다.

비용 기준 최적화에는 세 단계가 있습니다. 첫 번째는 트랜잭션 처리 단계라고 하며 트랜잭션 처리 시스템 특유의 간단한 요청에 대한 계획을 찾습니다. 일반적으로 이러한 요청은 일반 계획 최적화 프로그램에서 처리하는 요청보다 훨씬 복잡하며 계획들을 비교하여 가장 저렴한 계획을 찾는 작업이 필요합니다. 트랜잭션 처리 단계가 완료되면 SQL Server에서 내부 임계값과 발견한 가장 저렴한 계획의 비용을 비교합니다. 이 임계값은 앞으로의 최적화 허용 여부를 결정할 때 사용됩니다. 계획의 비용이 임계값 미만이면 이미 발견된 계획을 실행하는 것보다 추가 최적화를 실행하는 데 비용이 더 많이 듭니다. 따라서 SQL Server에서는 최적화를 중단하고 트랜잭션 처리 단계에서 발견한 계획을 사용합니다.

트랜잭션 처리 단계에서 발견한 계획이 이 단계의 임계값에 비해 비용이 많이 들면 SQL Server는 두 번째 단계로 이동합니다. 이 단계를 종종 QuickPlan 단계라고 합니다. QuickPlan 단계는 웬만큼 복잡한 쿼리에도 적절한 계획을 선택할 수 있도록 적절한 계획에 대한 검색 범위를 확장합니다. QuickPlan은 가능한 범위의 계획을 조사하고 이 작업이 완료되면 최적 계획의 비용을 두 번째 임계값과 비교합니다. 트랜잭션 처리 단계와 마찬가지로 임계값보다 낮은 비용의 계획이 발견되면 최적화가 중단되고 이 계획이 사용됩니다. 일반적으로 SQL Server 6.5에서 쿼리가 사용하려고 했던 계획이 SQL Server 7.0에서도 최적의 계획이면 일반 계획 최적화 프로그램이나 비용 기준 최적화의 처음 두 단계에서 계획이 발견될 것입니다. 이렇게 되도록 규칙을 의도적으로 구성하였습니다. 이 계획에서는 단일 인덱스 및 중첩된 루프 조인을 사용합니다.

전체 최적화라고 하는 최적화의 마지막 단계는 복잡한 쿼리에서부터 아주 복잡한 쿼리에 이르기까지의 쿼리에 대한 적절한 계획을 세우기 위한 단계입니다. 복잡한 쿼리의 경우 QuickPlan에서 만든 계획은 더 나은 계획을 계속 검색할 때보다 훨씬 많은 비용이 들 수 있기 때문에, 전체 최적화가 수행됩니다. 전체 최적화에서는 실제로 별도의 두 가지 옵션을 사용할 수 있습니다. QuickPlan 단계에서 산출되는 최적 비용이 "병렬 처리를 위한 비용 임계값"의 구성 값보다 크고 서버가 다중 프로세서 컴퓨터이면, 최적화 프로그램의 마지막 단계에는 다중 프로세서에서 병렬로 실행될 수 있는 계획을 찾는 작업이 수반됩니다. QuickPlan 단계에서 제공한 최적 계획의 비용이 구성된 "비용 임계값 병렬 처리" 미만이면 최적화 프로그램은 직렬 계획만 고려합니다. 계획은 이 마지막 단계에서 반드시 발견되어야 하므로 전체 최적화 단계는 소모적인 작업이 되어 시간이 많이 소요될 수 있습니다. 최적화 프로그램은 모든 잠재적 계획의 비용을 최적화의 이 단계에 이르는 데 소요될 비용과 비교하고 여러 최적화를 계속 시도하는 데 소요될 비용을 추정하므로 모든 가능한 계획을 검토하지 않을 수도 있습니다. 어느 단계에 이르면 최적화 프로그램은 더 나은 계획을 검색하는 것보다는 현재 계획을 사용하는 것이 더 저렴하며 최적화를 계속함으로써 추가 컴파일 비용을 지불하는 것은 비용면에서 효율적이지 않다고 결정할 수도 있습니다. 이 마지막 단계에서 처리되는 쿼리의 계획은 일반적으로 한 번만 사용되므로 이후의 실행에서 계획이 다시 사용될 때 컴파일과 최적화에 추가 비용이 들 가능성이 거의 없습니다. 이후의 실행이 거의 일어나지 않기 때문입니다.

계획이 발견되면 이 계획은 최적화 프로그램의 출력이 되고 SQL Server에서 계획을 실행하기 전에 지금까지 논의한 모든 캐싱 메커니즘을 수행합니다. 전체 최적화 프로그램 단계에서 해당 쿼리에 대한 병렬 계획을 제공한다고 해서 반드시 다중 프로세서에서 이 계획이 실행되지는 않습니다. 컴퓨터 사용량이 많고 여러 CPU에서 하나의 쿼리를 실행할 수 없으므로 이 계획은 단일 프로세서를 사용합니다.

그림 13에서는 최적화 프로그램을 통한 처리 흐름을 보여 줍니다.

사용자 삽입 이미지

그림 13. 최적화

실행

쿼리 처리의 마지막 단계는 실행입니다. 실행에 대해서는 간략하게 설명합니다. 실행 엔진은 최적화 프로그램에서 만든 계획을 가져와서 실행합니다. 실제 실행하면서 실행 엔진은 이 프로세스에 대한 스레드가 실행되도록 일정을 만들고 스레드 간 통신을 제공합니다.

요약

지금까지 설명한 대로 SQL Server 내부 및 아키텍처를 모두 다루기에는 너무 광범위하고 이 문서에서 제공하지 못한 부분도 많습니다. 이 문서에서는 SQL Server가 클라이언트와 상호 작용하는 방법 및 SQL Server 관계형 엔진이 클라이언트의 요청을 처리하는 방법을 중점적으로 설명하였습니다. SQL Server에서 쿼리를 처리하는 방법, 쿼리를 컴파일하거나 다시 컴파일하는 방법과 그 시기를 알게 됨으로써 SQL Server 7.0의 성능과 정교함을 이용하여 보다 뛰어난 응용 프로그램을 작성할 수 있기를 바랍니다.

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by mari