옵티마이 저가 비 클러스터형 인덱스 대신 클러스터형 인덱스 + 정렬을 선택하는 이유는 무엇입니까?

다음 예제를 보자.

IF OBJECT_ID('dbo.my_table') IS NOT NULL
    DROP TABLE [dbo].[my_table];
GO

CREATE TABLE [dbo].[my_table]
(
    [id]    int IDENTITY (1,1)  NOT NULL PRIMARY KEY,
    [foo]   int                 NULL,
    [bar]   int                 NULL,
    [nki]   int                 NOT NULL
);
GO

/* Insert some random data */
INSERT INTO [dbo].[my_table] (foo, bar, nki)
SELECT TOP (100000)
    ABS(CHECKSUM(NewId())) % 14,
    ABS(CHECKSUM(NewId())) % 20,
    n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM
    sys.all_objects AS s1
CROSS JOIN
    sys.all_objects AS s2
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
    ON [dbo].[my_table] ([nki] ASC);
GO

[nki](비 클러스터형 인덱스)에서 주문한 모든 레코드를 가져 오는 경우 :

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 266 ms, elapsed time = 493 ms

옵티마이 저는 클러스터형 인덱스를 선택한 다음 정렬 알고리즘을 적용합니다.

Execution plan

그러나 클러스터되지 않은 인덱스를 사용하도록 강요하면 :

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 311 ms, elapsed time = 188 ms

그런 다음 키 조회와 함께 비 클러스터형 인덱스를 사용합니다.

Execution plan

비 클러스터형 인덱스가 포함 인덱스로 변환 된 경우 :

CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
    ON [dbo].[my_table] ([nki] ASC)
    INCLUDE (id, foo, bar);
GO

그런 다음이 인덱스 만 사용합니다.

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 32 ms, elapsed time = 106 ms

Execution plan


질문

  • SQL Server가 후자의 경우 실행 시간이 38 % 더 빠르더라도 클러스터되지 않은 인덱스를 사용하는 대신 클러스터형 인덱스와 정렬 알고리즘을 사용하는 이유는 무엇입니까?


답변

SQL Server가 후자의 경우 실행 시간이 38 % 더 빠르더라도 클러스터되지 않은 인덱스를 사용하는 대신 클러스터형 인덱스와 정렬 알고리즘을 사용하는 이유는 무엇입니까?

SQL Server는 런타임 정보가 아닌 통계를 기반으로 비용 기반 최적화 프로그램을 사용하기 때문입니다.

이 쿼리에 대한 비용 추정 프로세스 중에 실제로 조회 계획을 평가하지만 더 많은 노력이 필요할 것으로 추정합니다. 실행 계획에서 SELECT 위로 마우스를 가져 가면 “예상 서브 트리 비용”에 유의하십시오. 테스트 시스템에서 조회 계획은 정렬 / 스캔 CPU의 6 배를 차지합니다.

SQL Server가 조회 계획 비용이 더 높은 이유에 대한 Rob Farley의 답변을 살펴보십시오.


답변

100,000 개 조회에 필요한 읽기 수와 정렬 작업과 관련된 읽기 수를 비교하는 경우 Query Optimizer가 CIX + Sort가 최선의 선택 인 이유를 신속하게 파악할 수 있습니다.

읽기 페이지가 메모리에 있기 때문에 조회 실행이 더 빨라집니다 (캐시를 지우더라도 페이지 당 많은 행이 있으므로 동일한 페이지를 반복해서 읽지 만 조각화 양은 다릅니다. 또는 다른 활동과 다른 메모리 압력이있는 경우에는 해당되지 않을 수 있습니다). CIX + Sort가 더 빨리 진행되는 데는 그렇게 많은 시간이 걸리지 않지만, 현재보고있는 것은 읽기 비용이 동일한 페이지를 반복적으로 치는 것의 상대적 저렴한 점을 고려하지 않기 때문입니다.


답변

나는이 질문에 대해 조금 파기하기로 결정했고 비 클러스터 클러스터 인덱스를 사용하지 않고 어떻게 그리고 언제 또는 더 나은지에 대해 이야기하는 흥미로운 문서를 발견했습니다.

에 의해 코멘트를 제안 존 Eisbrener 심지어 다른 사람의 블로그에서 가장 참조 중 하나, 킴벌리 L. 트립의이 흥미로운 기사입니다 :

관심이 있다면이 페이지를 살펴볼 수 있습니다.

보시다시피, 모두 티핑 포인트 개념을 따라 움직 입니다.

KL Tripp 기사에서 인용

티핑 포인트는 무엇입니까?

리턴 된 행 수가 ” 더 이상 선택적으로 충분하지 않은 “지점 입니다. SQL Server는 비 클러스터형 인덱스를 사용하여 해당 데이터 행을 조회하지 않고 테이블 스캔을 수행하도록 선택합니다.

SQL Server가 힙에서 비 클러스터형 인덱스를 사용하면 기본적으로 기본 테이블의 페이지에 대한 포인터 목록이 나타납니다. 그런 다음이 포인터를 사용하여 RID (Row ID Lookups)라는 일련의 작업으로 행을 검색합니다. 이것은 최소한 반환 된 행 수만큼 또는 더 많은 페이지 읽기를 사용한다는 것을 의미합니다. 이 프로세스는 클러스터 된 인덱스와 기본 테이블과 다소 유사하며 더 많은 읽기 결과를 얻을 수 있습니다.

그러나 티핑 포인트가 발생하면?

물론 이생의 대부분의 것들로서 그것은 …

심각하게도 페이지 당 행 수에 따라 표의 페이지 수의 25 % ~ 33 % 사이에서 발생합니다. 그러나 고려해야 할 더 많은 요소가 있습니다.

ITPRoToday 기사에서 인용

티핑 포인트에 영향을 미치는 다른 요소 RID 조회 비용이 티핑 포인트에 영향을 미치는 가장 중요한 요소이지만 여러 가지 다른 요소가 있습니다.

  • 클러스터 된 인덱스를 스캔 할 때 실제 I / O가 훨씬 효율적입니다. 클러스터 된 인덱스 데이터는 디스크에 인덱스 순서대로 순차적으로 배치됩니다. 결과적으로 디스크에 측면 헤드 이동이 거의 없으므로 I / O 성능이 향상됩니다.
  • 데이터베이스 엔진이 클러스터형 인덱스를 스캔 할 때 디스크 트랙의 다음 몇 페이지에 여전히 필요한 데이터가 포함될 가능성이 높다는 것을 알고 있습니다. 따라서 일반적인 8KB 페이지 대신 64KB 청크로 미리 읽기 시작합니다. 또한 I / O가 더 빨라집니다.

이제 통계 IO를 사용하여 쿼리를 다시 실행하면

SET STATISTICS IO ON;
SELECT id, foo, bar, nki FROM my_table WHERE nki < 20000 ORDER BY nki ;
SET STATISTICS IO OFF;

Logical reads: 312

SET STATISTICS IO ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS IO OFF;

Logical reads: 41293

두 번째 쿼리는 첫 번째 쿼리보다 더 많은 논리적 읽기가 필요합니다.

비 클러스터형 인덱스를 피해야합니까?

아니요, 클러스터형 인덱스는 유용 할 수 있지만 시간이 걸리고 달성하려는 목표를 분석하는 데 추가 노력을 기울일 가치가 있습니다.

KL Tripp 기사에서 인용

그래서 어떻게해야합니까? 때에 따라 다르지. 데이터를 잘 알고 있고 광범위한 테스트를 수행하는 경우 힌트 사용을 고려할 수 있습니다 (sps에서 프로그래밍 방식으로 수행 할 수있는 영리한 일이 있으므로 곧 게시 할 것입니다). 그러나 훨씬 더 나은 선택은 (가능한 경우) 커버링을 고려하는 것입니다 (실제로 내 요점입니다). 내 쿼리에서 모든 쿼리 (악한 SELECT *)를 원하기 때문에 커버링이 비현실적이지만 쿼리가 좁고 우선 순위가 높은 경우 힌트보다 커버링 인덱스 (많은 경우)를 사용하는 것이 좋습니다. 쿼리를 다루는 인덱스, 팁은 절대 없습니다.

그것은 지금 당장 퍼즐에 대한 해답이지만 확실히 더 많은 것들이 있습니다. 티핑 포인트는 매우 유용 할 수 있으며 일반적으로 잘 작동합니다. 그러나 색인을 작성하고 더 나은 성능을 얻을 수 있다는 것을 알게되면 조사를 수행하고 이것이 맞는지 확인할 수 있습니다. 그런 다음 힌트가 도움이 될 가능성을 고려하면 이제 집중할 수있는 위치를 알 수 있습니다.


답변