지난번 소개해 드린 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 |