임시 테이블이 열망하는 스풀보다 할로윈 문제에 대한 더 효율적인 솔루션 인 이유는 무엇입니까?


14

대상 테이블에 아직없는 경우에만 소스 테이블에서 행을 삽입하는 다음 쿼리를 고려하십시오.

INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT maybe_new_rows.ID
FROM dbo.A_HEAP_OF_MOSTLY_NEW_ROWS maybe_new_rows
WHERE NOT EXISTS (
    SELECT 1
    FROM dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR halloween
    WHERE maybe_new_rows.ID = halloween.ID
)
OPTION (MAXDOP 1, QUERYTRACEON 7470);

하나의 가능한 계획 형태에는 병합 조인 및 열성적인 스풀이 포함됩니다. 할로윈 문제 를 해결하기 위해 열망하는 스풀 연산자가 있습니다 .

첫 번째 계획

내 컴퓨터에서 위 코드는 약 6900ms 안에 실행됩니다. 테이블을 작성하는 Repro 코드는 질문 맨 아래에 포함되어 있습니다. 성능에 만족하지 않으면 열망 스풀에 의존하지 않고 임시 테이블에 삽입 할 행을로드하려고 시도 할 수 있습니다. 가능한 구현은 다음과 같습니다.

DROP TABLE IF EXISTS #CONSULTANT_RECOMMENDED_TEMP_TABLE;
CREATE TABLE #CONSULTANT_RECOMMENDED_TEMP_TABLE (
    ID BIGINT,
    PRIMARY KEY (ID)
);

INSERT INTO #CONSULTANT_RECOMMENDED_TEMP_TABLE WITH (TABLOCK)
SELECT maybe_new_rows.ID
FROM dbo.A_HEAP_OF_MOSTLY_NEW_ROWS maybe_new_rows
WHERE NOT EXISTS (
    SELECT 1
    FROM dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR halloween
    WHERE maybe_new_rows.ID = halloween.ID
)
OPTION (MAXDOP 1, QUERYTRACEON 7470);

INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT new_rows.ID
FROM #CONSULTANT_RECOMMENDED_TEMP_TABLE new_rows
OPTION (MAXDOP 1);

새로운 코드는 약 4400ms 안에 실행됩니다. 실제 계획을 세우고 Actual Time Statistics ™을 사용하여 운영자 수준에서 시간이 소비되는 위치를 조사 할 수 있습니다. 실제 계획을 요청하면 이러한 쿼리에 상당한 오버 헤드가 발생하므로 총계가 이전 결과와 일치하지 않습니다.

╔═════════════╦═════════════╦══════════════╗
  operator    first query  second query 
╠═════════════╬═════════════╬══════════════╣
 big scan     1771         1744         
 little scan  163          166          
 sort         531          530          
 merge join   709          669          
 spool        3202         N/A          
 temp insert  N/A          422          
 temp scan    N/A          187          
 insert       3122         1545         
╚═════════════╩═════════════╩══════════════╝

열망 스풀이있는 쿼리 계획은 임시 테이블을 사용하는 계획에 비해 삽입 및 스풀 연산자에 훨씬 더 많은 시간을 소비하는 것으로 보입니다.

임시 테이블이있는 계획이 더 효율적인 이유는 무엇입니까? 어쨌든 간절한 스풀은 대부분 내부 임시 테이블이 아닌가? 나는 내부에 중점을 둔 답변을 찾고 있다고 생각합니다. 통화 스택이 어떻게 다른지 알 수 있지만 큰 그림을 파악할 수는 없습니다.

누군가 알고 싶어하는 경우 SQL Server 2017 CU 11을 사용하고 있습니다. 위 쿼리에 사용 된 테이블을 채우는 코드는 다음과 같습니다.

DROP TABLE IF EXISTS dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR;

CREATE TABLE dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR (
ID BIGINT NOT NULL,
PRIMARY KEY (ID)
);

INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT TOP (20000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
CROSS JOIN master..spt_values t3
OPTION (MAXDOP 1);


DROP TABLE IF EXISTS dbo.A_HEAP_OF_MOSTLY_NEW_ROWS;

CREATE TABLE dbo.A_HEAP_OF_MOSTLY_NEW_ROWS (
ID BIGINT NOT NULL
);

INSERT INTO dbo.A_HEAP_OF_MOSTLY_NEW_ROWS WITH (TABLOCK)
SELECT TOP (1900000) 19999999 + ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

답변:


14

이것을 수동 할로윈 보호라고 합니다.

내 기사 업데이트 쿼리 최적화 에서 업데이트 명령문과 함께 사용되는 예제를 찾을 수 있습니다 . 예를 들어 시나리오에서 관련이있는 경우 별도의 쿼리가 실행되는 동안 모든 동시 수정에 대해 대상 테이블을 잠그는 등 동일한 의미를 유지하기 위해 약간 신중해야합니다.

임시 테이블이있는 계획이 더 효율적인 이유는 무엇입니까? 어쨌든 간절한 스풀은 대부분 내부 임시 테이블이 아닌가?

스풀은 임시 테이블의 특성 중 일부를 갖지만 둘은 정확히 동등한 것은 아닙니다. 특히, 스풀은 기본적 으로 b-tree 구조에 대한 행별 순서가없는 삽입 입니다. 잠금 및 로깅 최적화의 이점은 있지만 대량로드 최적화 는 지원하지 않습니다 .

결과적으로 쿼리를 자연스러운 방식으로 분할하여 더 나은 성능을 얻을 수 있습니다. 새로운 행을 임시 테이블 또는 변수로 대량로드 한 다음 임시 오브젝트에서 명시 적 Halloween Protection없이 최적화 된 삽입을 수행합니다.

이 분리를 사용하면 원래 명령문의 읽기 및 쓰기 부분을 개별적으로 조정할 수 있습니다.

참고로 행 버전을 사용하여 할로윈 문제를 해결하는 방법에 대해 생각하는 것이 흥미 롭습니다. 아마도 향후 버전의 SQL Server는 적절한 환경에서 해당 기능을 제공 할 것입니다.


마이클 쿠츠 (Michael Kutz)가 한 의견에서 언급 한 것처럼, 홀 필링 최적화 를 활용하여 명시적인 HP를 피할 수있는 가능성을 탐색 할 수도 있습니다 . 데모에서이를 달성하는 한 가지 방법은의 ID열에 고유 색인 (원하는 경우 클러스터 된)을 작성하는 것 입니다 A_HEAP_OF_MOSTLY_NEW_ROWS.

CREATE UNIQUE INDEX i ON dbo.A_HEAP_OF_MOSTLY_NEW_ROWS (ID);

이를 보장함으로써 옵티마이 저는 구멍 채우기 및 행 집합 공유를 사용할 수 있습니다.

MERGE dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (SERIALIZABLE) AS HICETY
USING dbo.A_HEAP_OF_MOSTLY_NEW_ROWS AS AHOMNR
    ON AHOMNR.ID = HICETY.ID
WHEN NOT MATCHED BY TARGET
THEN INSERT (ID) VALUES (AHOMNR.ID);

머지 플랜

흥미롭게도 신중하게 구현 된 수동 할로윈 보호 기능을 사용하면 많은 경우에 더 나은 성능을 얻을 수 있습니다.


5

폴의 답변을 약간 확장하기 위해 스풀과 임시 테이블 접근 간의 경과 시간 차이의 일부 DML Request Sort는 스풀 계획 의 옵션에 대한 지원이 부족한 것으로 보입니다 . 문서화되지 않은 추적 플래그 8795를 사용하면 임시 테이블 접근의 경과 시간이 4400ms에서 5600ms로 이동합니다.

INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT new_rows.ID
FROM #CONSULTANT_RECOMMENDED_TEMP_TABLE new_rows
OPTION (MAXDOP 1, QUERYTRACEON 8795);

이것은 스풀 계획에 의해 수행 된 삽입과 정확히 동일하지는 않습니다. 이 쿼리는 트랜잭션 로그에 훨씬 더 많은 데이터를 씁니다.

약간의 속임수와 같은 효과가 반대로 나타날 수 있습니다. 할로윈 보호를 위해 SQL Server가 스풀 대신 정렬을 사용하도록 권장 할 수 있습니다. 하나의 구현 :

INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT TOP (987654321) 
maybe_new_rows.ID
FROM dbo.A_HEAP_OF_MOSTLY_NEW_ROWS maybe_new_rows
WHERE NOT EXISTS (
    SELECT 1
    FROM dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR halloween
    WHERE maybe_new_rows.ID = halloween.ID
)
ORDER BY maybe_new_rows.ID, maybe_new_rows.ID + 1
OPTION (MAXDOP 1, QUERYTRACEON 7470, MERGE JOIN);

이제 계획에는 스풀 대신 TOP N 정렬 연산자가 있습니다. 정렬은 차단 연산자이므로 스풀이 더 이상 필요하지 않습니다.

여기에 이미지 설명을 입력하십시오

더 중요한 것은 이제 DML Request Sort옵션 을 지원한다는 것입니다. 실제 시간 통계를 다시 보면 삽입 연산자는 이제 1623ms 만 걸립니다. 실제 계획을 요청하지 않고 전체 계획을 실행하는 데 약 5400ms가 걸립니다.

Hugo가 설명 했듯이 Eager Spool 운영자는 순서를 유지합니다. 그것은 TOP PERCENT계획으로 가장 쉽게 볼 수 있습니다 . 스풀이있는 원래 쿼리가 스풀에있는 데이터의 정렬 된 특성을 더 잘 활용할 수 없다는 것은 불행한 일입니다.

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