모든 배치는 컴파일을 유발합니다


10

T-SQL 문을 일괄 적으로 보내는 타사 응용 프로그램이 있습니다.

데이터베이스는 SQL Server 2016 Enterprise SP1 CU7, 16 코어 및 256GB 메모리에서 호스팅됩니다. 임시 최적화가 활성화되었습니다.

다음은 실행중인 쿼리의 더미 예입니다.

exec sp_executesql N'
IF @@TRANCOUNT = 0 SET TRANSACTION ISOLATION LEVEL SNAPSHOT

select field1, field2 from table1 where field1=@1
option(keep plan, keepfixed, loop join)

select field3, field4 from table2 where field3=@1
option(keep plan, keepfixed, loop join)', N'@1 nvarchar(6)',@1=N'test'

데이터베이스를 모니터링하고 batchs / sec 및 compiles / sec를 보면 항상 동일하다는 것을 알았습니다. 로드가 많은 경우 초당 1000 배치 및 초당 1,000 개의 컴파일이 가능합니다. 평균로드에서 초당 150 개의 배치가 있습니다.

최근에 컴파일 한 계획에 대한 쿼리 캐시를 분석합니다.

SELECT TOP (1000) qs.creation_time
    , DatabaseName = DB_NAME(st.dbid)
    , qs.execution_count
    , st.text
    , qs.plan_handle
    , qs.sql_handle
    , qs.query_hash 
FROM sys.dm_exec_query_stats qs
    CROSS APPLY sys.dm_exec_sql_text(qs.plan_handle) AS st
ORDER BY creation_time DESC;

위의 쿼리를 실행하면 초당 10-20 개의 새 쿼리 계획 만 표시됩니다.

모든 sp_executesql호출이 컴파일을 트리거하는 것과 같지만 쿼리 계획은 캐시되지 않습니다.

batchs / sec가 compiles / sec와 같은 원인은 무엇입니까?

답변:


12

모든 sp_executesql호출이 컴파일을 트리거하는 것과 같지만 쿼리 계획은 캐시되지 않습니다.

SQL Server sp_executesql 호출 만 포함 된 배치에 대한 쿼리 계획을 캐시하지 않습니다 . 캐시 된 계획이 없으면 매번 컴파일이 수행됩니다. 이것은 의도적으로 설계된 동작입니다.

SQL Server는 컴파일 비용이 저렴한 배치 캐싱을 방지합니다. 캐시되고 캐시되지 않은 것에 대한 세부 사항은 수년에 걸쳐 여러 번 변경되었습니다. 추적 플래그 2861에 대한 나의 답변 과 실제로 '제로 비용'계획이 의미하는 바를 참조하십시오.

즉, 재사용 가능성 (특정 매개 변수 값 포함)이 적고 sp_executesql호출을 포함하는 임시 텍스트를 컴파일하는 비용 이 매우 적습니다. 내부 매개 변수가있는 배치 sp_executesql는 물론 캐시되고 재사용됩니다-이것이 그 가치입니다. 확장 저장 프로 시저 sp_executesql자체도 캐시됩니다.

캐시되고 재사용 되려면 sp_executesql명령문은 캐싱 할 가치가있는 더 큰 배치의 일부 여야합니다. 예를 들면 다음과 같습니다.

-- Show compilation counter
SELECT
    DOPC.[object_name],
    DOPC.cntr_value
FROM sys.dm_os_performance_counters AS DOPC
WHERE
    DOPC.counter_name = N'SQL Compilations/sec'
GO
-- This is only here to make the batch worth caching
DECLARE @TC integer =
(
    SELECT TOP (1) @@TRANCOUNT 
    FROM master.dbo.spt_values AS SV
);

-- Example call we are testing
-- (use anything for the inner query, this example uses the Stack Overflow database
EXECUTE sys.sp_executesql 
    N'SELECT LT.Type FROM dbo.LinkTypes AS LT WHERE LT.Id = @id;', 
    N'@id int', 
    @id = 1;
GO
-- Show compilation counter again
SELECT
    DOPC.[object_name],
    DOPC.cntr_value
FROM sys.dm_os_performance_counters AS DOPC
WHERE
    DOPC.counter_name = N'SQL Compilations/sec'

해당 코드를 여러 번 실행하십시오. 그러나 처음에는 많은 편집이 예상대로보고됩니다. 두 번째로, 사용하지 않으면 컴파일이보고되지 않습니다 optimize for ad hoc workloads(따라서 컴파일 된 계획 스텁 만 캐시 됨). 세 번째로, 스텁이 완전히 캐시 된 특별 계획으로 승격되기 때문에 어떤 경우에도 컴파일이보고되지 않습니다.

제거 DECLARE @TC있는지 문 sys.sp_executesql이 실행되는 횟수에 관계없이 문이없이 캐시되지 않습니다.

다음을 사용하여 연관된 계획 캐시 항목을보십시오.

-- Show cached plans
SELECT
    DECP.refcounts,
    DECP.usecounts,
    DECP.size_in_bytes,
    DECP.cacheobjtype,
    DECP.objtype,
    DECP.plan_handle,
    DECP.parent_plan_handle,
    DEST.[text]
FROM sys.dm_exec_cached_plans AS DECP
CROSS APPLY sys.dm_exec_sql_text(DECP.plan_handle) AS DEST
WHERE 
    DEST.[text] LIKE N'%sp_executesql%'
    AND DEST.[text] NOT LIKE N'%dm_exec_cached_plans%';

관련 Q & A : 트리거가 매번 컴파일됩니까?


11

아래와 같이 성능 모니터 및 활동 모니터에 표시되는 대략적인 값을 테스트로 별도의 쿼리 창에서 일부 배치를 실행하는 동안 SQL Compilations/secBatch Requests/sec에 대해 확인할 수 있습니다.

쿼리 창 1 :

DECLARE @t1 datetime;
DECLARE @t2 datetime;
DECLARE @CompVal1 int;
DECLARE @CompVal2 int;
DECLARE @ReCompVal1 int;
DECLARE @ReCompVal2 int;
DECLARE @BatchVal1 int;
DECLARE @BatchVal2 int;
DECLARE @ElapsedMS decimal(10,2);

SELECT @t1 = GETDATE()
    , @CompVal1 = (
        SELECT spi.cntr_value
        FROM sys.sysperfinfo spi
        WHERE spi.counter_name = 'SQL Compilations/sec                                                                                                            '
        )
    , @ReCompVal1 = (
        SELECT spi.cntr_value
        FROM sys.sysperfinfo spi
        WHERE spi.counter_name = 'SQL Re-Compilations/sec                                                                                                         '
        )
    , @BatchVal1 = (
        SELECT spi.cntr_value
        FROM sys.sysperfinfo spi
        WHERE spi.counter_name = 'Batch Requests/sec                                                                                                              '
        );

WAITFOR DELAY '00:00:10.000';

SELECT @t2 = GETDATE()
    , @CompVal2 = (
        SELECT spi.cntr_value
        FROM sys.sysperfinfo spi
        WHERE spi.counter_name = 'SQL Compilations/sec                                                                                                            '
        )
    , @ReCompVal2 = (
        SELECT spi.cntr_value
        FROM sys.sysperfinfo spi
        WHERE spi.counter_name = 'SQL Re-Compilations/sec                                                                                                         '
        )
    , @BatchVal2 = (
        SELECT spi.cntr_value
        FROM sys.sysperfinfo spi
        WHERE spi.counter_name = 'Batch Requests/sec                                                                                                              '
        );

SET @ElapsedMS = DATEDIFF(MILLISECOND, @t1, @t2);
SELECT  ElapsedTimeMS = @ElapsedMS
    , [SQL Compilations/sec] = (@CompVal2 - @CompVal1) / @ElapsedMS * 1000 
    , [SQL Recompilations/sec] = (@ReCompVal2 - @ReCompVal1) / @ElapsedMS * 1000
    , [Batch Requests/sec] = (@BatchVal2 - @BatchVal1) / @ElapsedMS * 1000;

쿼리 창 2에서 위 코드가 실행되는 동안 다음을 실행하십시오. 이 코드는 단순히 100 개의 T-SQL 배치를 실행합니다.

EXEC sys.sp_executesql N'SELECT TOP(1) o.name FROM sys.objects o;';
GO 100

쿼리 창 1로 다시 전환하면 다음과 같은 내용이 표시됩니다.

╔ ================ ╦ ================================== =============== ╦ =======================
lapse ElapsedTimeMS ║ SQL 컴파일 / 초 ║ SQL 재 컴파일 / 초 ║ 배치 요청 / 초 ║
╠ ================ ╬ ================================== =============== ╬ =======================
║ 10020.00 ║ 10.07984031000 ║ 0.00000000000 ║ 10.07984031000 ║
╚ ================ ╩ ======================== ╩ ========== =============== ╩ =======================

이 쿼리를 보면 :

SELECT dest.text
    , deqs.execution_count
FROM sys.dm_exec_query_stats deqs
    CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) dest
WHERE dest.text LIKE 'SELECT TOP(1)%'

테스트 쿼리가 100 번 실행되었음을 확인할 수 있습니다.

위의 결과 에서 명령문이 실행될 때마다 컴파일되고 있음을 알 수 있습니다 sp_executesql. 이를위한 계획은 확실히 캐시되고 있지만, 우리는 그것을위한 편집을 보게됩니다. 무엇을 제공합니까?

마이크로 소프트 문서는 이에 대해 말할 sp_executesql:

sp_executesql은 배치, 이름 범위 및 데이터베이스 컨텍스트와 관련하여 EXECUTE와 동일한 동작을 갖습니다. sp_executesql @stmt 매개 변수의 Transact-SQL 문 또는 일괄 처리는 sp_executesql 문이 실행될 때까지 컴파일되지 않습니다. 그런 다음 @stmt의 내용은 sp_executesql이라는 배치의 실행 계획과 별도로 실행 계획으로 컴파일 및 실행됩니다.

따라서 명령 텍스트에 대한 계획이 이미 계획 캐시에 있더라도 실행할 때마다 sp_executesql 자체 컴파일됩니다. @PaulWhite는 그의 대답 에서 sp_executesql에 대한 대부분의 호출이 실제로 캐시되지 않음을 보여줍니다 .

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