쿼리 계획 작성 비용을 측정하거나 찾는 방법은 무엇입니까?


18

매개 변수 스니핑으로 인해 "잘못된"실행 계획이 계획 캐시에 도착하여 저장 프로 시저의 후속 실행이 매우 느려지는 일반적인 경우가 있습니다. 로컬 변수 OPTIMIZE FOR ... UNKNOWN및 로이 문제를 "해결"할 수 있습니다 OPTION(RECOMPILE). 그러나 쿼리로 뛰어 들어 최적화하려고 할 수도 있습니다.

나는 여부를 결정하기 위해 노력하고있어 한다 수정 문제에 대한 제한 시간이 주어진 나는 비용 알고 싶습니다 : 하지 그 일을. 보시다시피, 그냥 고집 OPTION(RECOMPILE)하면 순 효과는 쿼리가 실행될 때마다 쿼리 계획이 다시 만들어지는 것 입니다. 그래서 알아야 할 것 같습니다.

쿼리 계획 을 만드는 데 드는 비용 이 얼마 인지 확인하는 방법 은 무엇입니까?

내 자신의 질문에 대답하기 위해 (예 : 이 쿼리로 ) Googled 를 작성했으며 dm_exec_query_statsDMV에 대한 열 문서를 살펴 보았습니다 . 이 정보를 찾기 위해 "실제 쿼리 계획"에 대한 SSMS의 출력 창을 검사했습니다. 마지막으로 DBA.SE를 검색했습니다 . 그 중 아무도 대답을 얻지 못했습니다.

누구든지 말해 줄 수 있습니까? 계획 수립에 필요한 시간을 찾거나 측정 할 수 있습니까?


5
Benjamin NevarezInside SQL Server Query Optimizer 사본을 가져 오는 것이 좋습니다 . 무료입니다. 5 장 '최적화 프로세스'는 쿼리의 컴파일 시간을 계산하는 데 도움이 될 수 있습니다. 최소한 쿼리 계획을 작성하기 위해 옵티마이 저가 수행하는 작업에 대한 정보를 제공합니다.
Mark Sinkinson

답변:


18

쿼리 계획을 만드는 데 드는 비용이 얼마인지 확인하는 방법은 무엇입니까?

쿼리 계획에서 루트 노드의 속성을 확인할 수 있습니다. 예를 들면 다음과 같습니다.

뿌리 속성 추출
(무료 Sentry One Plan Explorer 스크린 샷 )

이 정보는 계획 캐시를 쿼리하여 (예 : 다음 관계에 따라 쿼리를 사용하여) 사용할 수도 있습니다.

WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT 
    CompileTime = c.value('(QueryPlan/@CompileTime)[1]', 'int'),
    CompileCPU = c.value('(QueryPlan/@CompileCPU)[1]', 'int'),
    CompileMemory = c.value('(QueryPlan/@CompileMemory)[1]', 'int'),
    ST.[text],
    QP.query_plan
FROM sys.dm_exec_cached_plans AS CP
CROSS APPLY sys.dm_exec_query_plan(CP.plan_handle) AS QP
CROSS APPLY sys.dm_exec_sql_text(CP.plan_handle) AS ST
CROSS APPLY QP.query_plan.nodes('ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS N(c);

결과 조각

이러한 종류의 쿼리를 처리하는 데 필요한 옵션에 대한 전체 처리는 Erland Sommarskog의 최근 업데이트 된 기사를 참조하십시오 .


4

"비용"이 시간의 측면에 있다고 가정하면 (;-의 측면에서 다른 것이 무엇인지 확실하지는 않지만) 최소한 다음과 같은 작업을 수행하여이를 이해할 수 있어야합니다.

DBCC FREEPROCCACHE WITH NO_INFOMSGS;

SET STATISTICS TIME ON;

EXEC sp_help 'sys.databases'; -- replace with your proc

SET STATISTICS TIME OFF;

"메시지"탭에보고 된 첫 번째 항목은 다음과 같아야합니다.

SQL Server 구문 분석 및 컴파일 시간 :

이 작업을 최소 10 회 실행하고 평균 "CPU"및 "경과 된"밀리 초를 사용합니다.

이상적으로는 실제 시간을 예상 할 수 있도록 프로덕션 환경에서이를 실행하지만 프로덕션 환경에서 계획 캐시를 지울 수있는 사람은 거의 없습니다. 다행히 SQL Server 2008부터는 캐시에서 특정 계획을 지울 수있게되었습니다. 어떤 경우에는 다음을 수행 할 수 있습니다.

DECLARE @SQL NVARCHAR(MAX) = '';
;WITH cte AS
(
  SELECT DISTINCT stat.plan_handle
  FROM sys.dm_exec_query_stats stat
  CROSS APPLY sys.dm_exec_text_query_plan(stat.plan_handle, 0, -1) qplan
  WHERE qplan.query_plan LIKE N'%sp[_]help%' -- replace "sp[_]help" with proc name
)
SELECT @SQL += N'DBCC FREEPROCCACHE ('
               + CONVERT(NVARCHAR(130), cte.plan_handle, 1)
               + N');'
               + NCHAR(13) + NCHAR(10)
FROM cte;
PRINT @SQL;
EXEC (@SQL);

SET STATISTICS TIME ON;

EXEC sp_help 'sys.databases' -- replace with your proc

SET STATISTICS TIME OFF;

그러나, 값의 변화에 따라가 "나쁜"캐시 된 계획을 유발하는 매개 변수 (들)에 대한 전달되고, 그 사이의 중간 고려해야하는 또 다른 방법이있다 OPTION(RECOMPILE)OPTION(OPTIMIZE FOR UNKNOWN)동적 SQL :. 그렇습니다. 그리고 매개 변수가없는 Dynamic SQL도 의미합니다. 이유는 다음과 같습니다.

최소한 하나 이상의 입력 매개 변수 값과 관련하여 분포가 고르지 않은 데이터가 분명히 있습니다. 언급 된 옵션의 단점은 다음과 같습니다.

  • OPTION(RECOMPILE)모든 실행을위한 계획을 생성하고 당신이 혜택을 누릴 수 없을 것입니다 어느 다시 전달 된 매개 변수 값은 이전에 실행 (들)과 동일한 경우에도, 계획 재사용. 자주 (몇 초에 한 번 또는 더 자주) 호출되는 프로의 경우 가끔 끔찍한 상황에서 벗어날 수 있지만 항상 그다지 좋은 상황은 아닙니다.

  • OPTION(OPTIMIZE FOR (@Param = value)) 특정 값을 기반으로 계획을 생성하여 여러 경우에 도움이 될 수 있지만 여전히 현재 문제에 대해 개방적입니다.

  • OPTION(OPTIMIZE FOR UNKNOWN)평균 분포에 해당하는 금액을 기준으로 계획을 생성하여 일부 쿼리에 도움이되지만 다른 쿼리에는 피해를줍니다. 로컬 변수를 사용하는 옵션과 동일해야합니다.

그러나 동적 SQL 은 올바르게 수행되면 전달되는 다양한 값에 이상적인 별도의 쿼리 계획을 갖도록 허용합니다 (잘 될 것입니다). 여기서 주요 비용은 전달되는 다양한 값이 증가함에 따라 캐시의 실행 계획 수가 증가하고 메모리를 차지한다는 것입니다. 사소한 비용은 다음과 같습니다.

  • SQL 주입을 방지하기 위해 문자열 매개 변수의 유효성을 검사해야 함

  • 동적 SQL에는 테이블 직접 권한이 필요하므로 이상적인 보안 추상화를 유지하기 위해 인증서 및 인증서 기반 사용자를 설정해야 할 수도 있습니다.

그래서 여기 초당 1 회 이상 호출 된 프로세서가 있고 각각 수백만 개의 행이있는 여러 테이블에 부딪쳤을 때이 상황을 관리 한 방법이 있습니다. 나는 시도 OPTION(RECOMPILE)했지만 매개 변수 스니핑 / 잘못된 캐시 계획 문제가없는 99 %의 경우 프로세스에 너무 해로운 것으로 판명되었습니다. 이러한 proc 중 하나에는 약 15 개의 쿼리가 있으며 여기에 설명 된대로 3-5 개만 Dynamic SQL로 변환되었습니다. 특정 쿼리에 필요하지 않으면 동적 SQL이 사용되지 않았습니다.

  1. 스토어드 프로 시저에 여러 입력 매개 변수가있는 경우, 데이터 분산이 매우 높은 열에 사용되는 매개 변수 (이로 인해이 문제점이 발생 함)와 더 고른 분포가있는 열에 사용되는 매개 변수를 파악하십시오. 이 문제의 원인).

  2. 균일하게 분산 된 열과 연관된 proc 입력 매개 변수의 매개 변수를 사용하여 동적 SQL 문자열을 빌드하십시오. 이 매개 변수화는이 쿼리와 관련된 캐시에서 실행 계획의 결과 증가를 줄이는 데 도움이됩니다.

  3. 매우 다양한 분포와 관련된 나머지 매개 변수의 경우이 값을 리터럴 값으로 Dynamic SQL에 연결해야합니다. 고유 쿼리는 쿼리 텍스트에 대한 변경 사항에 따라 결정되므로을 갖는 WHERE StatusID = 1것과 다른 쿼리를 가지므로 쿼리 계획이 다릅니다 WHERE StatusID = 2.

  4. 쿼리 텍스트에 연결될 proc 입력 매개 변수 중 하나가 문자열 인 경우, SQL 주입을 방지하기 위해 유효성을 검증해야합니다 (전달되는 문자열이 다음에 의해 생성되는 경우 발생할 가능성은 적음) 앱이 아닌 사용자). 적어도 REPLACE(@Param, '''', '''''')작은 따옴표가 이스케이프 된 작은 따옴표가되도록하십시오.

  5. 필요한 경우, 사용자를 작성하는 데 사용될 인증서를 작성하고 스토어드 프로 시저에 서명하여 직접 테이블 권한이 새 인증서 기반 사용자에게만 부여되고 [public]그렇지 않으면 해당 권한이없는 사용자에게 부여되지 않도록합니다. .

proc 예 :

CREATE PROCEDURE MySchema.MyProc
(
  @Param1 INT,
  @Param2 DATETIME,
  @Param3 NVARCHAR(50)
)
AS
SET NOCOUNT ON;

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'
     SELECT  tab.Field1, tab.Field2, ...
     FROM    MySchema.SomeTable tab
     WHERE   tab.Field3 = @P1
     AND     tab.Field8 >= CONVERT(DATETIME, ''' +
  CONVERT(NVARCHAR(50), @Param2, 121) +
  N''')
     AND     tab.Field2 LIKE N''' +
  REPLACE(@Param3, N'''', N'''''') +
  N'%'';';

EXEC sp_executesql
     @SQL,
     N'@P1 INT',
     @P1 = @Param1;

답변 할 시간을 내 주셔서 감사합니다! 컴파일 시간을 얻는 방법에 대한 첫 번째 비트에 대해서는 약간 회의적이지만 @PaulWhite의 접근법 을 사용하여 얻은 결과보다 요소 3이 낮습니다 . -두 번째 on Dynamic SQL 비트는 흥미 롭습니다 (구현하는 데 시간이 필요하지만 적어도 쿼리를 때리는 것 이상 ). 이 sproc이 통합 테스트에 잘 활용되어 있기 때문에 너무 아프지 않습니다 . -어쨌든 : 당신의 통찰력에 감사드립니다! OPTION
Jeroen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.