열성 스풀 연산자가 클러스터 된 열 저장소에서이 삭제에 유용합니까?


28

클러스터 된 columnstore 인덱스에서 데이터 삭제를 테스트하고 있습니다.

실행 계획에 큰 열망 스풀 운영자가 있음을 알았습니다.

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

이것은 다음 특성으로 완료됩니다.

  • 6 천만 행 삭제
  • 1.9 GiB TempDB 사용
  • 14 분 실행 시간
  • 연속 계획
  • 스풀에 리 바인드 1 개
  • 예상 스캔 비용 : 364.821

견적을 과소 평가하도록 속이는 경우 TempDB를 사용하지 않는 더 빠른 계획을 얻습니다.

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

예상 스캔 비용 : 56.901

(이것은 추정 된 계획이지만 주석의 숫자는 정확합니다.)

흥미롭게도 다음을 실행하여 델타 저장소를 플러시하면 스풀이 다시 사라집니다.

ALTER INDEX IX_Clustered ON Fact.RecordedMetricsDetail REORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON);

스풀은 델타 저장소에 페이지 임계 값보다 많은 임계 값이있는 경우에만 소개됩니다.

델타 저장소의 크기를 확인하기 위해 다음 쿼리를 실행하여 테이블의 행 내 페이지를 확인합니다.

SELECT  
  SUM([in_row_used_page_count]) AS in_row_used_pages,
  SUM(in_row_data_page_count) AS in_row_data_pages
FROM sys.[dm_db_partition_stats] as pstats
JOIN sys.partitions AS p
ON pstats.partition_id = p.partition_id
WHERE p.[object_id] = OBJECT_ID('Fact.RecordedMetricsDetail');

첫 번째 계획에서 스풀 반복자에게 그만한 이점이 있습니까? 나는 그것이 존재하지 않기 때문에 할로윈 보호가 아닌 성능 향상을위한 것이라고 가정해야합니다.

2016 CTP 3.1에서 이것을 테스트하고 있지만 2014 SP1 CU3에서도 동일한 동작을 봅니다.

스키마와 데이터를 생성하고 여기 에서 문제를 보여주는 스크립트를 게시했습니다 .

이 문제는 문제를 일으킨 문제 (대형 스풀로 채워진 TempDB)에 대한 해결 방법이 있으므로이 시점에서 최적화 프로그램의 동작에 대한 호기심이 대부분입니다. 대신 파티션 전환을 사용하여 삭제하고 있습니다.


2
내가 시도 OPTION (QUERYRULEOFF EnforceHPandAccCard)하면 스풀이 사라집니다. HP가 "할로윈 보호"라고 생각합니다. 그러나 USE PLAN힌트 와 함께 계획을 사용하려는 시도는 실패합니다 ( OPTIMIZE FOR 해결 방법 에서 계획을 사용하려고 시도하는 것처럼 )
Martin Smith

감사합니다 @MartinSmith. 어떤 생각 AccCard일까요? 열 카디널리티 카디널리티 오름차순?
James L

1
@JamesLupolt 아니요 특히 설득력있는 것은 없습니다. 어쩌면 Acc는 Accumulate 또는 Access입니까?
Martin Smith

답변:


22

첫 번째 계획에서 스풀 반복자에게 그만한 이점이 있습니까?

이것은 "타당성있는"것으로 간주하지만 비용 모델에 따른 대답은 그렇습니다. 물론 옵티마이 저는 항상 가장 저렴한 요금제를 선택하기 때문에 이는 사실입니다.

실제 질문은 비용 모델이 스풀이있는 계획이없는 계획보다 훨씬 저렴한 계획을 고려하는 이유 입니다. 델타 저장소에 행을 추가하기 전에 스크립트에서 새 테이블에 대해 생성 된 예상 계획을 고려하십시오.

DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE);

이 계획의 예상 비용은 거대한 771,734 단위입니다 .

원래 계획

삭제는 대량의 임의 I / O를 초래할 것으로 예상되므로 비용은 거의 모두 클러스터형 인덱스 삭제와 관련이 있습니다. 이것은 모든 데이터 수정에 적용되는 일반적인 논리입니다. 예를 들어, b- 트리 인덱스에 대한 정렬되지 않은 수정 세트는 I / O 비용과 관련하여 거의 임의의 I / O를 초래한다고 가정합니다.

데이터 변경 계획에는 이러한 비용상의 이유로 순차 액세스를 촉진하는 순서로 행을 제시하는 정렬 기능이 있습니다. 이 경우 테이블이 분할되어 영향이 더욱 심해집니다. 실제로 매우 분할 된; 당신의 스크립트는 그들 중 15,000을 만듭니다. 매우 분할 된 테이블에 대한 임의의 업데이트는 특히 중간에 파티션 (행 세트)을 전환하는 가격에도 높은 비용이 부과되므로 비용이 많이 듭니다.

마지막으로 고려해야 할 주요 요소는 위의 간단한 업데이트 쿼리 ( 'update'는 삭제를 포함한 모든 데이터 변경 작업을 의미 함)는 "행 집합 공유"라는 최적화를 수행 할 수 있다는 것입니다. 여기서 동일한 내부 행 집합이 스캔 및 테이블 업데이트. 실행 계획에는 여전히 두 개의 별도 연산자가 표시되지만 그럼에도 불구하고 사용되는 행 집합은 하나뿐입니다.

나는이 최적화를 적용 할 수 있기 때문에 옵티마이 저가 단순히 명시 적으로 잠재적 인 이점을 고려하지 않는 코드 경로를 취한다는 것을 의미하기 때문에 이것을 언급합니다. 저가 임의 I / O 비용을 줄이기 위해 정렬 . 테이블이 b-tree 인 경우, 구조는 본질적으로 정렬되어 있기 때문에 의미가 있습니다. 따라서 행 집합을 공유하면 모든 잠재적 이점이 자동으로 제공됩니다.

중요한 결과는 업데이트 연산자의 원가 계산 논리가 기본 개체가 열 저장소 인 경우이 순서 이점 (순차 I / O 또는 기타 최적화 촉진)을 고려하지 않는다는 것입니다. 이는 열 저장소 수정이 제자리에서 수행되지 않기 때문입니다. 델타 저장소를 사용합니다. 따라서 비용 모델은 b- 트리에 대한 공유 행 집합 업데이트와 열 저장소 간의 차이를 반영합니다.

그럼에도 불구하고 (매우!) 파티션 된 컬럼 스토어의 특수한 경우에도 다음 순서로 이동하기 전에 하나의 파티션에 대한 모든 업데이트를 수행하는 것이 여전히 I / O 관점에서 유리할 수 있다는 점에서 순서가 유지되는 이점이 있습니다. .

표준 비용 논리는 여기에서 열 저장소에 재사용되므로 파티션 순서를 유지하는 계획 (각 파티션 내에서 순서는 아님)은 비용이 적게 듭니다. 업데이트 연산자에 정렬 된 입력을 요구하기 위해 문서화되지 않은 추적 플래그 2332를 사용하여 테스트 쿼리에서이를 확인할 수 있습니다. DMLRequestSort업데이트시 속성을 true로 설정하고 최적화 프로그램이 다음 파티션으로 이동하기 전에 한 파티션에 대한 모든 행을 제공하는 계획을 생성하도록합니다.

DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332);

이 계획의 예상 비용은 52.5174 단위로 매우 낮습니다 .

DMLRequestSort = 진정한 계획

이러한 비용 절감은 모두 업데이트시 예상 I / O 비용이 낮기 때문입니다. 소개 된 스풀은 다음과 같은 업데이트에 필요한 파티션 순서로 출력을 보장 할 수있는 것 외에는 유용한 기능을 수행하지 않습니다.DMLRequestSort = true (컬럼 저장소 인덱스의 직렬 스캔은이 보증을 제공 할 수 없음). 스풀 자체의 비용은 특히 업데이트시 비용이 비현실적으로 감소한 것과 비교하여 상대적으로 낮은 것으로 간주됩니다.

업데이트 연산자에 순서화 된 입력이 필요한지 여부는 쿼리 최적화 초기에 결정됩니다. 이 결정에 사용 된 휴리스틱은 문서화되지 않았지만 시행 착오를 통해 결정될 수 있습니다. 델타 상점의 크기는이 결정에 대한 입력으로 보입니다. 일단 선택되면 쿼리 컴파일에 대한 선택은 영구적입니다. USE PLAN성공할 힌트는 없습니다 . 계획의 대상이 업데이트에 대한 입력을 주문했거나 그렇지 않은 경우.

카디널리티 추정치를 인위적으로 제한하지 않고이 쿼리에 대한 저비용 계획을 얻는 다른 방법이 있습니다. 스풀을 피하기 위해 충분히 낮은 추정값은 아마도 DMLRequestSort가 거짓이되어 예상되는 임의 I / O로 인해 계획 비용이 매우 높아질 것입니다. 대안은 2332 (DMLRequestSort = true)와 함께 추적 플래그 8649 (병렬 계획)를 사용하는 것입니다.

DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332, QUERYTRACEON 8649);

결과적으로 파티션 별 배치 모드 병렬 스캔 및 주문 보존 (병합) 개더 스트림 교환을 사용하는 계획이 작성됩니다.

주문 삭제

하드웨어에서 파티션 순서의 런타임 효과에 따라 세 가지 중 가장 좋은 결과를 얻을 수 있습니다. 즉, 큰 수정은 열 저장소에서 좋은 아이디어가 아니므로 파티션 전환 아이디어가 거의 확실합니다. 긴 컴파일 시간에 대처할 수 있고 특히 파티션 수가 많은 경우 파티션 된 객체에서 흔히 볼 수있는 기발한 계획을 선택할 수 있습니다.

비교적 새로운 기능, 특히 한계에 근접한 많은 기능을 결합하는 것은 좋지 않은 실행 계획을 얻는 좋은 방법입니다. 옵티 마이저 지원의 깊이는 시간이 지남에 따라 향상되는 경향이 있지만 15,000 개의 컬럼 저장소 파티션을 사용하면 항상 흥미로운 시간에 살 수 있습니다.

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