인덱스가 더 빨리 실행되지 않고 경우에 따라 쿼리 속도가 느려집니다. 왜 그래야만하지?


34

속도를 높이기 위해 인덱스를 실험하고 있었지만 조인의 경우 인덱스가 쿼리 실행 시간을 개선하지 않고 경우에 따라 속도가 느려집니다.

테스트 테이블을 작성하고 데이터로 채우는 조회는 다음과 같습니다.

CREATE TABLE [dbo].[IndexTestTable](
    [id] [int] IDENTITY(1,1) PRIMARY KEY,
    [Name] [nvarchar](20) NULL,
    [val1] [bigint] NULL,
    [val2] [bigint] NULL)

DECLARE @counter INT;
SET @counter = 1;

WHILE @counter < 500000
BEGIN
    INSERT INTO IndexTestTable
      (
        -- id -- this column value is auto-generated
        NAME,
        val1,
        val2
      )
    VALUES
      (
        'Name' + CAST((@counter % 100) AS NVARCHAR),
        RAND() * 10000,
        RAND() * 20000
      );

    SET @counter = @counter + 1;
END

-- Index in question
CREATE NONCLUSTERED INDEX [IndexA] ON [dbo].[IndexTestTable]
(
    [Name] ASC
)
INCLUDE (   [id],
    [val1],
    [val2])

이제 쿼리 1이 개선되었습니다 (약간만 개선되었지만 일관성이 있음).

SELECT *
FROM   IndexTestTable I1
       JOIN IndexTestTable I2
            ON  I1.ID = I2.ID
WHERE  I1.Name = 'Name1'

인덱스가없는 통계 및 실행 계획 (이 경우 테이블은 기본 클러스터형 인덱스를 사용함) :

(5000 row(s) affected)
Table 'IndexTestTable'. Scan count 2, logical reads 5580, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 109 ms,  elapsed time = 294 ms.

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

이제 색인이 활성화 된 상태 :

(5000 row(s) affected)
Table 'IndexTestTable'. Scan count 2, logical reads 2819, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 94 ms,  elapsed time = 231 ms.

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

이제 인덱스로 인해 속도가 느려지는 쿼리 (테스트는 테스트 용으로 만 생성되므로 의미가 없음) :

SELECT I1.Name,
       SUM(I1.val1),
       SUM(I1.val2),
       MIN(I2.Name),
       SUM(I2.val1),
       SUM(I2.val2)
FROM   IndexTestTable I1
       JOIN IndexTestTable I2
            ON  I1.Name = I2.Name
WHERE   
       I2.Name = 'Name1'
GROUP BY
       I1.Name

클러스터형 인덱스가 활성화 된 경우 :

(1 row(s) affected)
Table 'IndexTestTable'. Scan count 4, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 1, logical reads 155106, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 17207 ms,  elapsed time = 17337 ms.

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

이제 색인이 비활성화 된 상태 :

(1 row(s) affected)
Table 'IndexTestTable'. Scan count 5, logical reads 8642, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 2, logical reads 165212, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 17691 ms,  elapsed time = 9073 ms.

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

질문은 :

  1. 인덱스가 SQL Server에서 제안되었지만 왜 상당한 차이로 인해 속도가 느려 집니까?
  2. 대부분의 시간이 걸리는 Nested Loop 조인은 무엇이며 실행 시간을 개선하는 방법은 무엇입니까?
  3. 내가 잘못하고 있거나 놓친 것이 있습니까?
  4. 기본 인덱스 (기본 키만 해당)를 사용하면 결합 테이블의 각 행에 대해 시간이 덜 걸리고 클러스터되지 않은 인덱스가있는 경우 결합이 행의 이름 열에 있기 때문에 결합 된 테이블 행을 더 빨리 찾을 수 있습니다. 색인이 작성되었습니다. 이는 쿼리 실행 계획에 반영되며 IndexA가 활성화 된 경우 Index Seek 비용이 적지 만 왜 여전히 느려 집니까? 또한 Nested Loop 왼쪽 외부 조인에서 속도 저하를 일으키는 원인은 무엇입니까?

SQL Server 2012 사용

답변:


23

인덱스가 SQL Server에서 제안되었지만 왜 상당한 차이로 인해 속도가 느려 집니까?

쿼리 최적화 프로그램에서 인덱스 제안을합니다. 기존 인덱스에서 제대로 제공되지 않는 테이블에서 논리적으로 선택 하면 "결측 인덱스"제안이 출력에 추가 될 수 있습니다. 이러한 제안은 기회 주의적입니다. 이들은 전체 쿼리 분석을 기반으로하지 않으며 더 넓은 고려 사항을 고려하지 않습니다. 기껏해야보다 유용한 인덱싱이 가능할 수 있으며 숙련 된 DBA가 살펴 봐야합니다.

누락 된 인덱스 제안에 대해 말해야 할 것은 옵티마이 저의 비용 산정 모델을 기반으로하며 옵티마이 저는 제안 된 인덱스가 쿼리 의 예상 비용을 얼마나 줄일 수 있는지에 따라 추정 합니다. 여기서 핵심 단어는 "모델"과 "추정치"입니다. 쿼리 최적화 프로그램은 하드웨어 구성 또는 기타 시스템 구성 옵션에 대해 거의 알지 못합니다. 모델은 대부분 대부분의 시스템에있는 대부분의 사람들에게 합리적인 계획 결과를 산출하는 고정 된 숫자를 기반으로합니다. 사용 된 정확한 비용 수치와 관련된 문제 외에도 결과는 항상 추정치이며 추정치가 잘못 될 수 있습니다.

대부분의 시간이 걸리는 Nested Loop 조인은 무엇이며 실행 시간을 개선하는 방법은 무엇입니까?

교차 결합 작업 자체의 성능을 향상시키기 위해 수행 할 작업은 거의 없습니다. 중첩 루프는 교차 결합에 가능한 유일한 물리적 구현입니다. 조인 내부의 테이블 스풀은 각 외부 행에 대해 내부를 다시 스캔하지 않도록 최적화됩니다. 이것이 유용한 성능 최적화인지 여부는 다양한 요소에 달려 있지만 테스트에서는 쿼리가 없으면 쿼리가 더 좋습니다. 다시 말하지만 이것은 비용 모델을 사용한 결과입니다. CPU 및 메모리 시스템의 성능 특성이 사용자의 시스템과 다를 수 있습니다. 테이블 스풀을 피하기위한 특정 조회 힌트는 없지만 스풀 유무에 관계없이 실행 성능을 테스트하는 데 사용할 수있는 문서화되지 않은 추적 플래그 (8690)가 있습니다. 이것이 실제 생산 시스템 문제라면 스풀이없는 계획은 TF 8690을 사용하여 생성 된 계획을 기반으로 계획 지침을 사용하여 강제 할 수 있습니다. 설치가 기술적으로 지원되지 않으며 추적 플래그가 바람직하지 않은 부작용을 일으킬 수 있으므로 프로덕션에서 문서화되지 않은 추적 플래그를 사용하지 않는 것이 좋습니다.

내가 잘못하고 있거나 놓친 것이 있습니까?

가장 중요한 것은 비 클러스터형 인덱스를 사용하는 계획이 옵티 마이저 모델에 따라 예상 비용이 낮지 만 실행 시간에 심각한 문제가 있다는 것입니다. 클러스터형 인덱스를 사용하여 계획에서 스레드 간 행 분포를 살펴보면 상당히 양호한 분포를 볼 수 있습니다.

스캔 계획

비 클러스터형 인덱스 검색을 사용하는 계획에서 작업은 전적으로 하나의 스레드에 의해 수행됩니다.

계획을 추구

이는 병렬 스캔 / 검색 작업에 의해 작업이 스레드간에 분산되는 방식의 결과입니다. 병렬 스캔이 인덱스 탐색보다 작업을 더 잘 분배하는 것은 아니지만 항상이 경우에 적용됩니다. 보다 복잡한 계획에는 스레드간에 작업을 재분배하기위한 교환 파티션 교환이 포함될 수 있습니다. 이 계획에는 그러한 교환이 없으므로 행이 스레드에 지정되면 모든 관련 작업이 동일한 스레드에서 수행됩니다. 실행 계획에서 다른 연산자에 대한 작업 분배를 보면 모든 작업이 인덱스 검색에 표시된 것과 동일한 스레드에 의해 수행됨을 알 수 있습니다.

스레드 간의 행 분배에 영향을주는 쿼리 힌트는 없으며, 중요한 것은 가능성을 알고 실행 계획에서 문제점을 발생시키는시기를 판별하기에 충분한 세부 사항을 읽을 수 있어야한다는 것입니다.

기본 인덱스 (기본 키만 해당)를 사용하면 결합 테이블의 각 행에 대해 시간이 덜 걸리고 클러스터되지 않은 인덱스가있는 경우 결합이 행의 이름 열에 있기 때문에 결합 된 테이블 행을 더 빨리 찾을 수 있습니다. 색인이 작성되었습니다. 이는 쿼리 실행 계획에 반영되며 IndexA가 활성화 된 경우 Index Seek 비용이 적지 만 왜 여전히 느려 집니까? 또한 Nested Loop 왼쪽 외부 조인에서 속도 저하를 일으키는 원인은 무엇입니까?

비 클러스터형 인덱스 계획이 예상보다 효율적일 가능성이 높아졌습니다. 실행 시간에 성능 문제를 설명하는 스레드 간 작업 분배가 열악합니다.

예제를 완성하고 내가 언급 한 것들 중 일부를 설명하기 위해 더 나은 작업 분배를 얻는 한 가지 방법은 임시 테이블을 사용하여 병렬 실행을 수행하는 것입니다.

SELECT
    val1,
    val2
INTO #Temp
FROM dbo.IndexTestTable AS ITT
WHERE Name = N'Name1';

SELECT 
    N'Name1',
    SUM(T.val1),
    SUM(T.val2),
    MIN(I2.Name),
    SUM(I2.val1),
    SUM(I2.val2)
FROM   #Temp AS T
CROSS JOIN IndexTestTable I2
WHERE
    I2.Name = 'Name1'
OPTION (FORCE ORDER, QUERYTRACEON 8690);

DROP TABLE #Temp;

결과적으로보다 효율적인 인덱스 검색을 사용하고 테이블 스풀을 제공하지 않으며 작업을 스레드간에 잘 분산시키는 계획이 생성됩니다.

최적의 계획

내 시스템에서이 계획은 Clustered Index Scan 버전보다 훨씬 빠르게 실행됩니다.

병렬 쿼리 실행의 내부에 대해 더 자세히 알고 싶다면 PASS Summit 2013 세션 기록보고 싶을 것 입니다.


0

그것은 실제로 인덱스의 문제가 아니며, 더 잘못 작성된 쿼리입니다. 이름의 고유 한 값은 100 개 뿐이며 이름 당 고유 카운트는 5000입니다.

따라서 표 1의 각 줄에 대해 표 2에서 5000을 조인합니다. 25020004 줄을 말할 수 있습니까?

이것을 시도하십시오, 이것은 당신이 나열한 하나의 색인 만 있음에 유의하십시오.

    DECLARE @Distincts INT
    SET @Distincts = (SELECT  TOP 1 COUNT(*) FROM IndexTestTable I1 WHERE I1.Name = 'Name1' GROUP BY I1.Name)
    SELECT I1.Name
    , @Distincts
    , SUM(I1.val1) * @Distincts
    , SUM(I1.val2) * @Distincts
    , MIN(I2.Name)
    , SUM(I2.val1)
    , SUM(I2.val2)
    FROM   IndexTestTable I1
    LEFT OUTER JOIN

    (
        SELECT I2.Name
        , SUM(I2.val1) val1
        , SUM(I2.val2) val2
        FROM IndexTestTable I2
        GROUP BY I2.Name
    ) I2 ON  I1.Name = I2.Name
    WHERE I1.Name = 'Name1'
    GROUP BY  I1.Name

그리고 시간 :

    SQL Server parse and compile time: 
       CPU time = 0 ms, elapsed time = 8 ms.
    Table 'IndexTestTable'. Scan count 1, logical reads 31, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

     SQL Server Execution Times:
       CPU time = 0 ms,  elapsed time = 1 ms.

    (1 row(s) affected)
    Table 'IndexTestTable'. Scan count 2, logical reads 62, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

     SQL Server Execution Times:
       CPU time = 16 ms,  elapsed time = 10 ms.

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

잘못 구성된 쿼리에 대해 SQL 인덱스를 비난 할 수 없습니다


1
대답에 감사드립니다. 그렇습니다. 질문을 향상시킬 수는 있지만 제 질문의 논리는 기본 인덱스 (기본 키 만)를 사용하여 각 행에 대해 시간이 덜 걸리고 클러스터되지 않은 인덱스가있는 이유는 무엇입니까 조인 테이블, 조인 된 테이블 행은 더 빨리 발견되어야합니다. 이는 쿼리 실행 계획에 반영되며 IndexA가 활성화되어있을 때 Index Seek 비용이 더 적지 만 왜 여전히 느립니까? 또한 Nested Loop 왼쪽 외부 조인에서 속도 저하를 일으키는 원인은 무엇입니까? 질문을보다 명확하게하기 위해이 주석을 추가하기 위해 질문을 편집했습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.