전산이야기

SSMS 에서 Ctrl + M 을 눌러서 '실제 실행 계획 포함' 이 활성화 된 상태에서

쿼리를 실행 시키고 나면 실행계획이 함께 나타납니다.

실행계획 상의 특정 노드에 마우스를 가져가면 상세한 내용이 나타 납니다.

위 그림에서 '예상 행 수' 가 2.77684로 되어 있어 옵티마이저가 2.7행을 예상 했다고 생각할 수 있으나

실제 예상 행 수는 '예상 실행 횟수'를 곱한 값이 됩니다.

따라서 해당 노드에서 반환되는 총 예상 행 수는 2.77684 × 6985.3104807 = 19397.08956 입니다.

이 총 예상 행 수 값이 노드 하단의 분모 값 입니다.

그리고 분자 값은 '실제 행 수' 입니다. 

괄호 안의 %값은 총 예상 행 수 대비 실제 행 수의 백분율 입니다. 이 백분율 값이 100%에 가까울 수록

통계값에 의한 적절한 실행계획이 수립되었다고 볼 수 있습니다.

 

그러나 아래의 경우 처럼 이 값이 과도하게 적거나 많을 경우 통계가 제대로 갱신되지 않아서 엉뚱한 실행계획이

수립되었을 가능성이 있습니다.

이 경우 UPDATE STATISTICS 로 최신 통계로 업데이트가 필요 합니다.

Comment +0

아시는 바와 같이 임시 테이블은 #(로컬) 및 ##(전역)이 prefix로 붙은 테이블 입니다.
그리고 테이블변수는 TABLE 타입으로 DECLARE된 로컬변수 입니다.
 
둘 간에 여러가지 차이점이 있지만 여기서는 성능적인 이슈만 언급 하겠습니다.
테이블변수의 문제는 SQL서버가 통계정보를 생성하지 않기 때문에 적절한 실행계획 수립이 안될 수 있다는 점 입니다.
 
아래는 테이블변수를 사용한 특정 SP의 실행계획 입니다 (처리속도는 14초).

NL JOIN 으로 처리되어 과도한 Execute, Rows가 나왔습니다. SQL 서버가 NL을 선택한 것은 예상 행수가 모두 1 이기 때문 입니다.
예상 행수가 1:1 이기 때문에 NL로 처리해도 아무 문제가 없다고 판단이 되었습니다.
실제 테이블변수의 행은 1067 입니다.
 
이제 다른것은 그대로 두고 테이블변수를 임시테이블로만 변경한 후, 다시 실행계획을 확인 해봅니다 (처리속도는 6초).

이번에는 HASH JOIN 으로 처리 되었습니다. 이유는 예상행수가 1067, 617 이기 때문 입니다.
1067건과 617건을 NL JOIN 으로 처리하면 부하가 크다고 판단하고 HASH로 JOIN 전략을 변경한 겁니다.
 
결론적으로 일반적으로 행수가 적은 경우는 테이블변수를 사용해도 무관하지만, 크기가 큰 경우는 임시테이블을 사용해야 성능이슈가 발생하지 않게 됩니다.
 
 

 

Comment +0

SP는 최초(처음) 실행 하는 시점에 실행계획을 수립하고 이후에는 실행계획을 계속 재사용 하게 됩니다

(이것이 SP를 사용하는 중요한 이유 중 하나 입니다).

이 최초 실행계획 수립시, 인수로 넘겨진 파라미터(매개변수)를 기준으로 통계를 통해 예상 행수를 추측하게 됩니다.

그런데 넘겨진 파라미터가 아닌 로컬 변수를 이용해서 쿼리를 실행하면 옵티마이저가 행수 추측이 실패하게 되어

원하지 않는 실행 계획이 생성 될 수 있습니다.

이로 인하여 SP 성능이 저하 될 수 있습니다.

 

예를 들어 다음과 같이 SP 가 되어 있을 때,

create proc Proc1 (
@p_date varchar(8)
)
as
SELECT
   *
FROM 
   SDSaleMaster a
WHERE
   a.SaleDate = @p_date

SP를 호출 하면,

EXEC Proc1 @p_date = '20190601'

실행계획에서 행수를 1326으로 예측 합니다.

이는 통계와 일치하는 제대로 된 예상 행 수 이기 때문에, 실행계획이 적합하게 나올 가능성이 높습니다.

그런데 다음과 같이 매개변수가 아닌 로컬 변수를 사용 하게 되어 있을 때,

create proc proc1 (
@p_date varchar(8)
)
as
declare
   @v_date varchar(8)

set @v_date = convert(varchar(8), dateadd(d, -1, @p_date), 112)  -- 하루 전

SELECT
   *
FROM 
   SDSaleMaster a
WHERE
   a.SaleDate = @v_date

SP를 호출 하면,

 EXEC Proc1 @p_date = '20190602'

옵티마이저가 예상 행수를 제대로 추정할 수가 없어서 549로 나오는 데, 이는 해당 테이블의 평균 밀도값을 이용한 것 입니다.

평균밀도

예상 행수 = 밀도 * 테이블 전체 row 수

 

이 글이 도움이 되셨으면 좋아요 눌러 주시거나 댓글을 남겨 주세요.

Comment +0