2013. 2. 15. 11:08

지난번 소개해 드린 Parameter Sniffing의 해결방법(?)에 대해서 공유합니다.

 

결론부터 말씀 드리면 3가지 정도가 있습니다.

1.     추적 플래그 4136 설정

2.     옵티마이저 쿼리힌트 설정

3.     프로시저 내 WITH RECOMPILE 옵션 추가

 

이번에도 마찬가지로 데모를 통해 하나하나 눈으로 확인해 보죠~

 

* 추적 플래그 4136 설정

Use AdventureWorks

go

 

--쿼리실행 시간 및 통계설정

SET STATISTICS TIME ON;

SET STATISTICS IO ON;

 

DBCC TRACEON(4136, -1); --4136 추적 플래그 전역으로 ON!

- 추적 플래그는 SQL Server가 재시작 되면 초기화 되므로 시작 시 매개변수로 지정해서 자동으로 설정 가능

 

--SQL Server 캐시삭제(모든실행계획삭제)

DBCC FREEPROCCACHE

 

--인덱스 검색 후 풀스캔

EXEC user_GetCustomerShipDates '2001-07-10', '2001-07-11' -- 인덱스검색

EXEC user_GetCustomerShipDates '2001-07-08', '2004-08-07' -- 풀스캔(모든구간검색)

 

추적 플래그를 설정하기 전 인덱스 검색 후 풀스캔 했을 때 논리적 읽기 수가 94468 이였던 것에 반해 약 800배 이상이 감소했습니다.

참고로 추적 플래그 4136은 아래 버전이상에서만 포함되어 있습니다.

(This option was added in SQL Server 2008 SP1 CU7,

and SQL Server 2008 R2 CU2, and also back-ported into SQL Server 2005 in SP3 CU9)

 

추적 플래그 작동 방법을 요약하면 들어온 파라미터 값으로 통계 히스토그램(statistics histogram)을 통해

반환될 행 수를 미리 계산해서 실행하게 됩니다.

하지만 이와 같은 경우 정상적인 프로시저도 영향을 미칠 수 있으므로 Parameter Sniffing이 해결되지 않은

최후의 방법으로 사용해야 된다고 합니다.

 

* 옵티마이저 쿼리힌트 설정

DBCC TRACEOFF(4136, -1); --추적 플래그 OFF!

 

ALTER PROCEDURE [dbo].[user_GetCustomerShipDates]

(

@ShipDateStart DATETIME ,

@ShipDateEnd DATETIME

)

AS

SELECT  CustomerID ,

SalesOrderNumber

FROM Sales.SalesOrderHeader

WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd

OPTION ( OPTIMIZE FOR ( @ShipDateStart = '2001-07-08',

@ShipDateEnd = '2004-08-07' ))

 

실행계획을 FOR 이후 일자를 이용해서 생성하도록 옵션을 설정합니다.

위에서는 어떤 범위가 값이 들어오던지 풀 스캔 하도록 했습니다~

 

--SQL Server 캐시삭제(모든실행계획삭제)

DBCC FREEPROCCACHE

 

-- 인덱스 검색 후 풀스캔

EXEC user_GetCustomerShipDates '2001-07-10', '2001-07-11' -- 인덱스검색

EXEC user_GetCustomerShipDates '2001-07-08', '2004-08-07' -- 풀스캔(모든구간검색)

 

결과는 풀스캔 때와 동일하므로 따로 캡쳐하지 않았습니다.

 

* 프로시저 내 WITH RECOMPILE 옵션 추가

ALTER PROCEDURE [dbo].[user_GetCustomerShipDates]

(

@ShipDateStart DATETIME ,

@ShipDateEnd DATETIME

)

WITH RECOMPILE –프로시저가 호출될 때 마다 실행 계획 재작성

AS

SELECT CustomerID ,

                   SalesOrderNumber

FROM Sales.SalesOrderHeader

WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd

 

위와 같이 RECOMPILE 옵션 추가 후 프로시저를 호출해 보겠습니다.

 

--SQL Server 캐시삭제(모든실행계획삭제)

DBCC FREEPROCCACHE

 

--이번엔 반대로 인덱스 검색 후 풀스캔

EXEC user_GetCustomerShipDates '2001-07-10', '2001-07-11' -- 인덱스검색

EXEC user_GetCustomerShipDates '2001-07-08', '2004-08-07' -- 풀스캔(모든구간검색)

 

오오 파라미터 스니핑이 해결된 건 물론이고 각각 실행했을 때에 맞는(인덱스 탈 때 타고 풀스캔 해야 될 때 하는)

논리적 읽기 수를 보여주고 있네요. 실행계획도 정상적이고요~

 

그럼 피라미터 스니핑을 방지하기 위해서 모든 프로시저 내에 RECOMPILE 옵션 추가해도 될까요?

- 대답은 No!! 입니다 ㅎㅎ

 

맨 처음 들어가면서 프로시저의 큰 장점 중 하나가 실행계획의 재사용이라고 했는데요.

RECOMPILE 옵션을 사용하게 되면 정상적인 프로시저 또한 실행계획을 재사용하지 못하고 호출할 때 마다

실행계획을 다시 세우기 때문에 그만큼 비용이 낭비되게 됩니다.

하지만 파라미터 스니핑이 발생하는 프로시저에는 RECOMPILE 옵션을 추가하는 방법을 고려할 수 있습니다.

 

추가적인 RECOMPILE 옵션 Tip을 드리자면 Multiple queries로 되어있는 프로시저 내에는

문제가 되는 쿼리에만 RECOMPILE 옵션을 따로 지정할 수 있습니다.

 

CREATE PROCEDURE user_GetCustomerShipDates

(

@ShipDateStart DATETIME ,

@ShipDateEnd DATETIME

)

AS

SELECT CustomerID ,

SalesOrderNumber

FROM Sales.SalesOrderHeader

WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd

OPTION ( RECOMPILE ) –쿼리 단위로 옵션 설정 가능

GO

 

이상으로 Parameter Sniffing의 원인과 해결방법 까지 모두 살펴보았습니다.


Reference : Troubleshooting SQL Server A Guide for the Accidental DBA

'SQL Server > Tips' 카테고리의 다른 글

SQL Server CPU Usage 메일링  (0) 2013.02.20
SQL Server ISNULL & COALESCE 비교  (0) 2013.02.15
SQL Server Parameter Sniffing  (0) 2012.12.13
SQL Server Missing Indexes  (0) 2012.12.13
SQL Server Backup all Databses  (0) 2012.12.13
Posted by 니노G