OPTION (RECOMPILE)은 항상 빠릅니다. 왜?


169

OPTION (RECOMPILE)쿼리에 추가 하면 0.5 초 안에 실행되는 반면, 생략하면 쿼리는 5 분 이상 걸리는 이상한 상황이 발생했습니다.

쿼리를 통해 쿼리 분석기 또는 내 C # 프로그램에서 쿼리를 실행하는 경우 SqlCommand.ExecuteReader()입니다. 전화 (또는 전화하지 않음) DBCC FREEPROCCACHE또는 DBCC dropcleanbuffers차이가 없습니다. 쿼리 결과는 항상 OPTION (RECOMPILE)5 분을 초과하지 않고 항상 즉시 반환 됩니다. 이 테스트를 위해 쿼리는 항상 동일한 매개 변수로 호출됩니다.

SQL Server 2008을 사용하고 있습니다.

SQL 작성에 상당히 익숙하지만 OPTION이전에는 쿼리에서 명령을 사용한 적이 없으며이 포럼의 게시물을 검색 할 때까지 계획 캐시의 전체 개념에 익숙하지 않았습니다. 게시물에서 내 이해 OPTION (RECOMPILE)는 비싼 작업 이라는 것입니다 . 분명히 쿼리에 대한 새로운 조회 전략을 만듭니다. 그렇다면 왜 생략하는 후속 쿼리 OPTION (RECOMPILE)가 너무 느립니까? 후속 쿼리에서 재 컴파일 힌트가 포함 된 이전 호출에서 계산 된 조회 전략을 사용하지 않아야합니까?

모든 단일 호출에서 재 컴파일 힌트를 요구하는 쿼리를 갖는 것이 매우 드문 일입니까?

초급 질문에 대해 죄송하지만 실제로 이것의 머리 나 꼬리를 만들 수는 없습니다.

업데이트 : 쿼리를 게시하라는 요청을 받았습니다 ...

select acctNo,min(date) earliestDate 
from( 
    select acctNo,tradeDate as date 
    from datafeed_trans 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_money 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_jnl 
    where feedid=@feedID and feedDate=@feedDate 
)t1 
group by t1.acctNo
OPTION(RECOMPILE)

Query Analyzer에서 테스트를 실행할 때 다음 행을 앞에 추가하십시오.

declare @feedID int
select @feedID=20

declare @feedDate datetime
select @feedDate='1/2/2009'

내 C # 프로그램에서 호출하면 매개 변수가 SqlCommand.Parameters속성 을 통해 전달됩니다 .

이 논의의 목적으로, 매개 변수가 절대 변하지 않는다고 가정 할 수 있으므로 원인으로 최적이 아닌 매개 변수 냄새를 배제 할 수 있습니다.


3
쿼리에 대한 매개 변수는 무엇입니까? 이 기사를 확인하십시오. blogs.msdn.com/b/turgays/archive/2013/09/10/… 기본적으로 SQL은 proc가 처음 컴파일 될 때 매개 변수를 기반으로 쿼리 계획을 생성하려고 시도합니다. 다른, 아마도 더 현실적인 매개 변수를 전달하기 시작할 때 최적이 아닌 계획을 생성 할 수 있습니다
Sparky

3
검색어가 여기에 나열하기에 간결합니까? Sparky가 정확하고 아마도 매개 변수 스니핑과 관련이 있다고 생각합니다.이 훌륭한 기사를 읽을 때까지 나름대로
Chris

1
그러나이 경우 (이 테스트를 위해) 항상 동일한 매개 변수를 전달합니다. 다른 앱은 몰래 다른 매개 변수를 사용하여 쿼리를 호출 할 수 없었습니다. 기사 주셔서 감사합니다. 검토합니다.
채드 데커

2
이는 매개 변수 및 변수의 값을 스니핑하거나 더 단순화하기 때문에 발생할 수 있습니다. 더 큰의 단순화의 예는 붕괴 될 것 X = @X OR @X IS NULL으로 X=@X하고 추구 수행 여기 참조 또는 윈도우 함수와 전망에 대해 더 아래 술어를 밀어
마틴 스미스

3
편집 후 Query Analyzer 예제는 매개 변수가 아닌 변수를 사용합니다. 이들을 제외하고는 그 값이 스니핑되지 않습니다 RECOMPILE. 어쨌든 실행 계획을 포착하고 차이점을 살펴보십시오.
Martin Smith

답변:


157

사용 OPTION(RECOMPILE)이 의미 가있는 시간이 있습니다 . 내 경험상 유일하게 동적 SQL을 사용할 때 이것이 가능한 옵션입니다. 이것이 당신의 상황에 맞는지 탐구하기 전에 통계를 재구성하는 것이 좋습니다. 다음을 실행하여 수행 할 수 있습니다.

EXEC sp_updatestats

그런 다음 실행 계획을 다시 작성하십시오. 이를 통해 실행 계획이 작성 될 때 최신 정보를 사용하게됩니다.

추가하면 OPTION(RECOMPILE)쿼리가 실행될 때마다 실행 계획이 다시 작성됩니다. 나는 그것이 묘사 된 것을들은 적이 creates a new lookup strategy없지만 아마도 같은 용어에 대해 다른 용어를 사용하고있을 것입니다.

저장 프로 시저가 만들어 질 때 (.NET에서 ad-hoc sql을 호출한다고 생각하지만 매개 변수가있는 쿼리를 사용하는 경우 저장 프로 시저 호출이됩니다 ) SQL Server는이 쿼리에 대한 가장 효과적인 실행 계획을 결정하려고합니다. 데이터베이스의 데이터 및 전달 된 매개 변수 ( parameter sniffing )를 기반으로이 계획을 캐시합니다. 즉, 데이터베이스에 10 개의 레코드가있는 쿼리를 생성 한 다음 100,000,000 개의 레코드가있는 경우 실행하면 캐시 된 실행 계획이 더 이상 가장 효과적이지 않을 수 있습니다.

요약하면- OPTION(RECOMPILE)여기에 도움이 될 이유가 없습니다 . 통계와 실행 계획을 업데이트해야한다고 생각합니다. 통계 재 구축은 상황에 따라 DBA 작업의 필수 부분이 될 수 있습니다. 통계를 업데이트 한 후에도 여전히 문제가 발생하면 두 실행 계획을 모두 게시하는 것이 좋습니다.

그리고 귀하의 질문에 대답하기 위해-예, 최선의 선택이 쿼리를 실행할 때마다 실행 계획을 다시 컴파일하는 것은 매우 드문 일입니다.


22
예, sp_updatestats가 트릭을 수행했습니다. 처음에 10 개의 레코드가있는 테이블에서 쿼리가 실행되었다고 언급했을 때 머리에 못 박히고 이제는 테이블에 수백만 개의 레코드가 있습니다. 바로 내 경우였다. 나는 그것이 중요하다고 생각하지 않았기 때문에 게시물에서 언급하지 않았습니다. 매혹적인 물건. 다시 감사합니다.
채드 데커

3
테이블 변수로 작업 할 수있는 유일한 방법입니다. becuase SQL은 항상 하나의 행이 있다고 생각합니다. 수천 행이 포함되어 있으면 문제가됩니다.
Alex Zhukovskiy

4
한 가지 흥미로운 세부 사항 : 통계 업데이트는 이러한 통계를 사용하는 모든 캐시 된 계획을 암시 적으로 무효화하지만 업데이트 조치 후에 통계가 실제로 변경된 경우에만 유효 합니다 . 따라서 고도로 치우친 읽기 전용 테이블의 경우 명시 적 OPTION (RECOMPILE)해결책이 유일한 해결책 인 것 같습니다 .
Groo

141

쿼리 실행마다 급격한 차이가있을 때 종종 5 가지 문제 중 하나라는 것을 알았습니다.

  1. 통계-통계가 오래되었습니다. 데이터베이스는 테이블 및 인덱스의 다양한 열에 값 유형의 범위 및 분포에 대한 통계를 저장합니다. 이를 통해 쿼리 엔진은 쿼리를 수행하는 방법에 대한 "계획"공격을 개발하는 데 도움이됩니다 (예 : 해시를 사용하거나 전체 세트를 살펴 보는 테이블 간의 키를 일치시키는 데 사용되는 방법 유형). 전체 데이터베이스 또는 특정 테이블 또는 인덱스에서 업데이트 통계를 호출 할 수 있습니다. 통계가 최신 상태가 아닌 경우 동일한 쿼리에 대해 새로 삽입되거나 변경된 데이터에 대해 쿼리 계획이 최적의 상태가 아닐 가능성이 높기 때문에 한 실행에서 다른 실행으로 쿼리 속도가 느려집니다 (나중에 설명 함). 샘플링 할 데이터의 양에 따라 약간의 오버 헤드, 속도 저하 및 지연이 발생하므로 프로덕션 데이터베이스에서 통계를 즉시 업데이트하는 것이 적절하지 않을 수 있습니다. 전체 스캔 또는 샘플링을 사용하여 통계를 업데이트하도록 선택할 수도 있습니다. 쿼리 계획을 보면 다음 명령을 사용하여 사용중인 인덱스에 대한 통계를 볼 수도 있습니다.DBCC SHOW_STATISTICS (테이블 이름, 인덱스 이름) . 쿼리 계획에서 접근 방식을 기반으로 사용하는 키의 분포와 범위가 표시됩니다.

  2. PARAMETER SNIFFING- 캐시 된 쿼리 계획은 쿼리 자체가 변경되지 않은 경우에도 전달중인 특정 매개 변수에 적합하지 않습니다. 예를 들어 1,000,000 개 행 중 10 개만 검색하는 매개 변수를 전달하면 생성 된 쿼리 계획에 해시 조인이 사용될 수 있지만, 전달한 매개 변수가 1,000,000 개 행 중 750,000 개를 사용하는 경우 생성 된 계획은 인덱스 스캔 또는 테이블 스캔. 이러한 상황에서 SQL 문에 OPTION (RECOMPILE) 옵션을 사용 하거나 SP에 WITH RECOMPILE을 사용하도록 지시 할 수 있습니다. 엔진에 알리기 위해 이것은 "단일 사용 계획"이며 적용되지 않는 캐시 된 계획을 사용하지 마십시오. 이 결정을 내리는 방법에 대한 규칙은 없으며 사용자가 쿼리를 사용하는 방법을 아는 것에 달려 있습니다.

  3. INDEXES- 쿼리는 변경되지 않았지만 매우 유용한 인덱스 제거와 같은 다른 위치로 변경하면 쿼리 속도가 느려질 수 있습니다.

  4. ROWS CHANGED- 쿼리하는 행이 호출마다 크게 변경됩니다. 이 경우 일반적으로 통계가 자동으로 업데이트됩니다. 그러나 동적 SQL을 작성하거나 타이트한 루프 내에서 SQL을 호출하는 경우 잘못된 행 수 또는 통계 수에 따라 오래된 쿼리 계획을 사용하고있을 가능성이 있습니다. 이 경우에도 OPTION (RECOMPILE) 이 유용합니다.

  5. 논리 그 로직, 쿼리가 더 이상 효율적입니다, 그것은 행의 작은 숫자지만, 더 이상 스케일 좋았다. 여기에는 일반적으로 쿼리 계획에 대한보다 심층적 인 분석이 포함됩니다. 예를 들어, 더 이상 대량으로 작업을 수행 할 수 없지만 작업을 청크하고 더 작은 커밋을 수행해야합니다. 또는 교차 제품이 더 작은 세트에 대해서는 양호했지만 이제는 더 큰 규모로 CPU와 메모리를 차지합니다. DISTINCT를 사용하면 모든 행에 대해 함수를 호출합니다. CASTING 유형 변환 또는 NULLS 또는 함수 때문에 키 일치가 인덱스를 사용하지 않습니다. 여기에 너무 많은 가능성이 있습니다.

일반적으로 쿼리를 작성할 때 특정 데이터가 테이블 내에 어떻게 분포되어 있는지 대략적으로 파악해야합니다. 예를 들어, 열은 균등하게 분산 된 수의 서로 다른 값을 가질 수 있거나, 시간의 80 %가 분포가 시간이 지남에 따라 자주 변하거나 상당히 정적인지 여부에 관계없이 특정 값 세트를 가질 수 있습니다. 이렇게하면 효율적인 쿼리를 작성하는 방법에 대한 더 나은 아이디어가 제공됩니다. 그러나 쿼리 성능을 디버깅 할 때 왜 느리거나 비효율적 인 가설을 세우는 근거가 있습니다.


2
덕분에 친구. 이것은 훌륭한 정보입니다. 처음에 질문을 올렸을 때 귀하의 답변을 이해할 수 없었지만 지금은 저에게 완벽합니다.
채드 데커

3
PARAMETER SNIFFING은 현재까지 존재하는 가장 큰 위험입니다. 인터뷰에 실패 할 때까지이 명령에 대해 몰랐습니다. 매개 변수 스니핑에 대한 나의 해결책은 항상 매개 변수 값을 해시하고 "AND {hash} = {hash}"를 추가하여 SQL이 항상 다른 값을 갖도록하는 것입니다. 해킹이지만 작동했습니다.
Jeremy Boyd

27

OPTION (RECOMPILE)이 매우 도움이 될 수있는 상황 (@CodeCowboyOrg에서 제공)의 우수 목록에 추가하려면,

  1. 테이블 변수 . 테이블 변수를 사용하는 경우 테이블 변수에 대해 사전 빌드 된 통계가 없으므로 쿼리 계획에서 예상 행과 실제 행간에 큰 차이가 발생합니다. 테이블 변수가있는 쿼리에서 OPTION (RECOMPILE)을 사용하면 관련된 행 번호를 훨씬 더 잘 추정하는 쿼리 계획을 생성 할 수 있습니다. OPTION (RECOMPILE)을 추가 할 때까지 사용할 수 없었고 포기할 테이블 변수를 특히 비판적으로 사용했습니다. 실행 시간은 몇 시간에서 몇 분으로 단축되었습니다. 아마도 이례적인 일은 아니지만 어쨌든 테이블 변수를 사용하고 최적화 작업을 수행하는 경우 OPTION (RECOMPILE)이 차이를 만드는지 확인하는 것이 좋습니다.

1
5 개의 테이블 변수가있는 쿼리가 있습니다. 내 컴퓨터에서는 30 분 이상 실행됩니다. 내 동료 컴퓨터에서 <1 초 안에 실행됩니다. 컴퓨터는 유사한 하드웨어와 동일한 SQL Server 버전을 가지고 있습니다. 둘 다 OPTION (RECOMPILE)을 추가하면 두 시스템에서 2 초 안에 실행됩니다. 모든 경우에 실행 테스트는 SSMS에서 수행됩니다. 이 차이를 일으키는 원인은 무엇입니까?
Adam

1
Option (재 컴파일)없이 컴퓨터와 동료 컴퓨터에서 실행 계획을 비교할 수 있습니까? 차이의 원인을 보여줄 수 있습니다.
DWright

1
임시 테이블의 경우 동일한 상황입니까?
Muflix

1
@ muflix : 좋은 질문입니다. 나는 통계가 있고 엔진이 다른 테이블과 마찬가지로 자동 재 컴파일 선택을해야하기 때문에 임시 테이블의 효과가 동일하다고 생각하지 않습니다 (그러나 확실하지는 않습니다). 다른 사람이 더 확실하게 알고있을 수도 있습니다.
DWright

2
임시 테이블의 통계는 자동으로 업데이트되거나 다시 컴파일되지 않으므로 프로그래머가이를 수행해야합니다.
J. Michael Wuerth 1

1

쿼리를 시작하기 전에 가장 먼저해야 할 일은 인덱스와 통계를 조각 모음 / 재 구축하는 것입니다.

실행 계획이 안정적인지 (매개 변수를 변경할 때와 동일한 지) 확인하기 위해 실행 계획을 확인해야합니다. 그렇지 않은 경우 커버 인덱스 (이 경우 각 테이블에 대해)를 작성해야 할 수도 있습니다 (시스템을 통해 다른 쿼리에도 유용합니다).

예를 들면 : 색인 idx01_datafeed_trans 작성 datafeed_trans (feedid, feedDate) INCLUDE (acctNo, tradeDate)

계획이 안정적이거나 안정화 할 수있는 경우 sp_executesql ( 'sql sentence')로 문장을 실행하여 고정 된 실행 계획을 저장하고 사용할 수 있습니다.

계획이 불안정하면 임시 계획 또는 EXEC ( 'sql sentence')를 사용하여 매번 실행 계획을 평가하고 작성해야합니다. (또는 재 컴파일 된 저장 프로 시저).

도움이 되길 바랍니다.


1

이 질문을 무시하지만 아무도 고려하지 않은 것 같습니다.

통계-통계를 사용할 수 없거나 오도하는 통계

다음이 모두 해당되는 경우 :

  1. feedid 및 feedDate 열은 서로 관련이있을 수 있습니다 (예 : 피드 ID가 피드 날짜보다 더 구체적이고 날짜 매개 변수가 중복 정보 임).
  2. 두 열 모두를 순차 열로하는 인덱스는 없습니다.
  3. 이 두 열 모두를 다루는 수동으로 생성 된 통계는 없습니다.

그런 다음 SQL Server는 열이 서로 관련이 없다고 가정하여 제한 사항과 잘못된 실행 계획을 모두 적용 할 때 예상되는 카디널리티 예상보다 낮게 선택 될 수 있습니다. 이 경우 수정은 두 열을 연결하는 통계 개체를 만드는 것인데, 이는 비싼 작업이 아닙니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.