인덱스의 열 순서는 얼마나 중요합니까?


173

인덱스 선언의 시작 부분에서 가장 선택적인 열을 넣어야한다고 들었습니다. 예:

CREATE NONCLUSTERED INDEX MyINDX on Table1
(
   MostSelective,
   SecondMost,
   Least
)

우선, 내가 말하고있는 것이 맞습니까? 그렇다면 인덱스의 열 순서를 다시 정렬하여 성능에 큰 차이가 있습니까? 그렇지 않으면 "행하기 좋은"방법입니까?

내가 묻는 이유는 DTA를 통해 쿼리를 넣은 후 기존 인덱스와 거의 동일한 열이 거의 동일한 인덱스를 다른 순서로 생성하는 것이 좋습니다. 누락 된 열을 기존 색인에 추가하고 잘 호출하는 것을 고려하고있었습니다. 생각?

답변:


193

다음과 같은 색인을보십시오.

Cols
  1   2   3
-------------
|   | 1 |   |
| A |---|   |
|   | 2 |   |
|---|---|   |
|   |   |   |
|   | 1 | 9 |
| B |   |   |
|   |---|   |
|   | 2 |   |
|   |---|   |
|   | 3 |   |
|---|---|   |

첫 번째 열이 두 번째 열을 먼저 제한하는 것보다 더 많은 결과를 제거하므로 A를 먼저 제한하는 방법을 참조하십시오. 인덱스 통과 방법, 열 1, 열 2 등을 파악하면 주먹 패스에서 대부분의 결과를 제거하면 2 단계가 훨씬 빨라집니다.

다른 경우, 열 3에서 쿼리 한 경우 결과 집합을 좁히는 데 전혀 도움이되지 않기 때문에 옵티마이 저는 인덱스를 사용하지도 않습니다. 쿼리 할 때마다 다음 단계 전에 처리 할 결과 수를 좁 히면 성능이 향상됩니다.

인덱스도 이런 식으로 저장되므로 인덱스를 쿼리 할 때 첫 번째 열을 찾기 위해 인덱스를 역 추적하지 않습니다.

한마디로 : 아니오, 그것은 보여주기위한 것이 아니며 실제 성능상의 이점이 있습니다.


13
위의 그림에서 해당 인덱스는 열 1이 쿼리에 지정된 경우에만 유용합니다. 쿼리가 조인 또는 검색 조건 자에서 열 2 만 지정하면 도움이되지 않습니다. 따라서 순서도 중요합니다. 어쩌면 그것은 말할 필요도 없지만 언급하고 싶었습니다.
CodeCowboyOrg

3
또한 인덱스가 위의 그림과 같고 쿼리가 column1 및 column2에서 필터링한다고 가정하지만 column2가 더 독특하고 실제로 필터링하려는 것은 실제로 column2이므로 인덱스가있는 것이 더 유리합니다. 열 2가 첫 번째입니다. 이것은 직관적이지 않은 것처럼 보일 수 있지만 인덱스는 여러 페이지에 저장되고 값 범위가있는 트리입니다. 위의 1 열은 가능성의 1/2을 무시하고 인덱스는 이미 어떤 인덱스 페이지로 바로 갈지 알고 있습니다 Column2 값, 세트를 좁히기 위해 Column 1이 필요하지 않습니다.
CodeCowboyOrg

4
이 그림은 인덱스 구성 및 탐색 방법을 정확하게 나타내지 않습니다. 이 stackoverflow.com/a/39080819/73226를
Martin Smith

6
@MartinSmith 나는 그것이 부정확하다는 것에 동의하지 않습니다. 그것은 분명히 매우 단순화 된 것으로, 그것은 나의 의도였습니다. 그러나 더 깊이 파고 싶어하는 사람들에게는 레벨에 대해 훨씬 자세하게 파고 든 답변이 높이 평가됩니다. 당신이 당신의 나무 이미지를 보면 매우 간단한 방법으로 내가 보여주는 것을 볼 수 있습니다. 이것은 매우 독특하거나 심지어 SQL에 고유하지 않습니다. B- 트리 인덱싱은 많은 것들에서 일반적입니다.
Nick Craver

@MartinSmith 또한 부정확하다고 동의하지 않습니다. 설명하는 것은 인덱스를 다루는 방법의 표준 동작입니다. 범위 쿼리를 수행하면 옵티마이 저가하는 인덱스 페이지 수가 최소화되므로 선택성이 훨씬 중요합니다. 스캔해야합니다. 이것은 수백만 개의 행이있는 큰 테이블에서 중요 할 수 있습니다.
Paul Hatcher

127

열 순서가 중요합니다. 이제 올바른 순서는 쿼리 방법에 따라 다릅니다. 정확한 검색 또는 범위 스캔을 수행하기 위해 인덱스를 사용할 수 있습니다. 정확한 탐색은 인덱스의 모든 열에 대한 값이 지정되고 쿼리가 정확히 행에 도달하는 경우입니다. 탐색의 경우 열 순서는 관련이 없습니다. 범위 스캔은 일부 열만 지정된 경우이며이 경우 순서가 중요합니다. SQL Server는 가장 왼쪽 열이 지정된 경우에만 다음으로 가장 왼쪽 열이 지정된 경우에만 범위 검색에 인덱스를 사용할 수 있습니다. 당신은 (A, B, C)에 대한 인덱스가있는 경우이를위한 범위 스캔에 사용할 수 A=@a에 대한, A=@a AND B=@b하지만 하지 에 대한 B=@b위해, C=@cB=@b AND C=@c. 케이스 A=@a AND C=@cA=@a부분은 인덱스를 사용하지만 C=@cnot 은 사용합니다 (쿼리는 모든 B 값을 스캔하고로 A=@a건너 뛰지 않습니다 C=@c). 다른 데이터베이스 시스템에는 소위 '건너 뛰기 스캔'연산자가있어 외부 열이 지정되지 않은 경우 인덱스의 내부 열을 활용할 수 있습니다.

그 지식을 가지고 인덱스 정의를 다시 볼 수 있습니다. 인덱스 (MostSelective, SecondMost, Least)MostSelective컬럼이 지정된 경우에만 유효 합니다. 그러나 이것이 가장 선택 적이기 때문에 내부 컬럼의 관련성이 빠르게 저하됩니다. 더 나은 색인이 켜져 (MostSelective) include (SecondMost, Least)있거나 켜져 있는 경우가 종종 있습니다 (MostSelective, SecondMost) include (Least). 내부 열은 관련성이 적기 때문에 인덱스의 올바른 위치에 낮은 선택도 열을 배치하면 탐색에 노이즈가 발생하지 않으므로 중간 페이지 밖으로 이동하여 리프 페이지에만 유지하는 것이 좋습니다. 쿼리 적용 범위 목적. 즉, INCLUDE로 옮깁니다. Least열 크기가 커질수록 더 중요해 집니다. 이 인덱스는 다음을 지정하는 쿼리에만 혜택을 줄 수 있습니다.MostSelective 정확한 값 또는 범위로, 그리고 가장 선택적인 컬럼은 이미 후보 행을 상당히 제한합니다.

반면에 인덱스 (Least, SecondMost, MostSelective)는 실수로 보일 수 있지만 실제로는 매우 강력한 인덱스입니다. Least가장 바깥 쪽 쿼리로 열 이 있기 때문에 선택도가 낮은 열에 대한 결과를 집계해야하는 쿼리에 사용할 수 있습니다. 이러한 쿼리는 OLAP 및 분석 데이터웨어 하우스에서 널리 사용되며, 이러한 인덱스가 매우 적합한 경우입니다. 이러한 인덱스는 실제로 관련 클러스터의 큰 청크 ( Least일반적으로 일종의 범주 또는 유형을 나타내는 동일한 값) 에 물리적 레이아웃을 구성하고 분석 쿼리를 용이하게하기 때문에 우수한 클러스터형 인덱스를 만듭니다 .

불행히도 '정확한'순서는 없습니다. 쿠키 커터 레시피를 따르지 말고 대신 해당 테이블에 대해 사용할 쿼리 패턴을 분석하고 올바른 인덱스 열 순서를 결정하십시오.


3
평소 Remus와 같은 멋진 반응. 나는 당신의 세 번째 단락을 몇 번 더 읽고 후속 조치를 취할 것입니다. 나는 그것이 내가해야 할 일이라고 생각합니다.
Abe Miessler

"SQL Server는 가장 왼쪽 열이 지정된 경우에만 다음으로 가장 왼쪽 열이 지정된 경우에만 범위 검색에 인덱스를 사용할 수 있습니다." 이것은 내 이해에서 누락 된 것입니다, 감사합니다! 가장 많이 사용되는 인덱스 열에서만 범위 스캔을 수행 할 수 있다는 것을 알지 못했지만 이제는 그렇게합니다.
Allon Guralnek

이 설명이 Oracle DB에도 적용됩니까?
다른

1
@Roizpi 네, 기본적으로 인덱스가있는 관계 데이터베이스는 동일하거나 매우 유사한 방식으로 작동합니다.
Tatranskymedved

45

Remus에 따르면 워크로드에 따라 다릅니다.

그래도 수용 된 답변의 오도 된 측면을 다루고 싶습니다.

인덱스의 모든 열에서 동등 검색을 수행하는 쿼리의 경우 큰 차이가 없습니다.

아래는 두 개의 테이블을 만들고 동일한 데이터로 채 웁니다. 유일한 차이점은 하나의 키는 가장 선택적인 순서에서 가장 덜 선택적인 순서이고 다른 하나는 반대 순서입니다.

CREATE TABLE Table1(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);
CREATE TABLE Table2(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);

CREATE NONCLUSTERED INDEX MyINDX on Table1(MostSelective,SecondMost,Least);
CREATE NONCLUSTERED INDEX MyINDX2 on Table2(Least,SecondMost,MostSelective);

INSERT INTO Table1 (MostSelective, SecondMost, Least)
output inserted.* into Table2
SELECT TOP 26 REPLICATE(CHAR(number + 65),800), number/5, '~'
FROM master..spt_values
WHERE type = 'P' AND number >= 0
ORDER BY number;

이제 두 테이블 모두에 대해 쿼리를 수행합니다 ...

SELECT *
FROM   Table1
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~';

SELECT *
FROM   Table2
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~'; 

... 둘 다 지수 벌금을 사용하며 모두 동일한 비용이 부과됩니다.

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

허용 된 답변의 ASCII 기술은 실제로 색인이 구성되는 방식이 아닙니다. Table1의 인덱스 페이지는 아래와 같습니다 (이미지를 클릭하면 전체 크기로 열림).

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

인덱스 페이지에는 전체 키를 포함하는 행이 포함되어 있습니다 (이 경우 인덱스가 고유 한 것으로 선언되지 않았지만 이에 대한 자세한 정보는 여기서 무시할 수 있으므로 행 식별자에 추가 된 추가 키 열 이 있습니다 ).

위의 쿼리에서 SQL Server는 열의 선택성에 신경 쓰지 않습니다. 이 루트 페이지와 것을 발견의 이진 검색 수행 (PPP...,3,~ ) 입니다 >=(JJJ...,1,~ )< (SSS...,3,~ )그래서 페이지를 읽어야합니다 1:118. 그런 다음 해당 페이지에서 주요 항목을 이진 검색하고 아래로 이동할 리프 페이지를 찾습니다.

선택성 순서로 색인을 변경해도 이진 검색에서 예상되는 키 비교 수 또는 색인 검색을 수행하기 위해 탐색해야하는 페이지 수에는 영향을 미치지 않습니다. 기껏해야 키 비교 속도 약간 빨라질 수 있습니다.

때로는 가장 선택적인 인덱스를 먼저 주문하면 작업 부하의 다른 쿼리에 적합합니다.

예를 들어 작업 부하에 다음 두 가지 형식의 쿼리가 모두 포함되어있는 경우

SELECT * ... WHERE  MostSelective = 'P'

SELECT * ...WHERE Least = '~'

위의 색인 중 하나를 다루지 않습니다. MostSelective검색 및 조회 기능을 사용하여 계획을 세우기에 충분히 선택적이지만 쿼리에 Least대해서는 그렇지 않습니다.

그러나이 시나리오 (복합 인덱스의 선행 열 서브 세트에서 인덱스 탐색을 다루지 않음)는 인덱스가 도움이 될 수있는 쿼리 클래스 중 하나 일뿐입니다. 실제로 MostSelective단독으로 검색하지 않고 MostSelective, SecondMost항상 세 열 모두의 조합으로 검색하는 경우이 이론적 인 장점은 쓸모가 없습니다.

반대로 다음과 같은 쿼리

SELECT MostSelective,
       SecondMost,
       Least
FROM   Table2
WHERE  Least = '~'
ORDER  BY SecondMost,
          MostSelective 

쿼리를 다루고 탐색을 지원하고 원하는 순서로 행을 반환하여 부팅 할 수 있도록 일반적으로 처방 된 순서의 역순을 가하면 도움이됩니다.

이 조언의 자주 반복되는 부분이다 그러나 대부분에 그것의 잠재적 인 혜택에 대한 경험적 그래서 다른 쿼리 - 그리고 실제로보고를 대신 할 수 없습니다 당신의 작업.


31

인덱스 선언의 시작 부분에서 가장 선택적인 열을 넣어야합니다.

옳은. 인덱스는 복합 열일 수 있으며 여러 열로 구성되며 순서는 가장 왼쪽 원칙으로 인해 중요합니다. 데이터베이스가 목록을 왼쪽에서 오른쪽으로 확인하고 정의 된 순서와 일치하는 해당 열 참조를 찾아야하기 때문입니다. 예를 들어, 열이있는 주소 테이블에 색인이있는 경우 :

  • 주소
  • 시티
  • 상태

address열을 사용하는 모든 쿼리 는 인덱스를 사용할 수 있지만 쿼리에 참조 city및 / 또는 state참조 만있는 경우 인덱스를 사용할 수 없습니다. 가장 왼쪽 열이 참조되지 않기 때문입니다. 쿼리 성능은 개별 인덱스 또는 순서가 다른 여러 복합물 중 어느 것이 최적인지 알려줍니다. 읽어보기 : Kimberley Tripp 의 Tipping Point


사용되지 않은 가장 오른쪽 열인 경우 어떻게합니까? 따라서 쿼리는 주소와 도시를 사용했지만 상태는 아닙니다. 그러면 인덱스가 사용됩니까?
Abe Miessler

@Abe : 가장 오른쪽은 사용되지 않습니다. 왼쪽부터 시작하여 인덱스 순서를 만족시켜야합니다. 하나를 놓치면 사용할 수 없습니다.
OMG Ponies

4
@Abe : 주소와 도시를 쿼리했지만 상태가 아님-예인 경우 인덱스가 사용됩니다. 다시 말해, 데이터베이스는 인덱스의 왼쪽에서 시작하여 쿼리중인 필드를 사용하여 오른쪽으로 이동할 수있는 한 부분 인덱스를 사용하여 요청을 충족시킬 수 있습니다. 그러나 주소와시 /도를 사용하여 쿼리했지만 도시가 아닌 경우 여전히 인덱스를 사용할 수 있지만 효율적이지 않습니다. 이제 인덱스의 주소 부분 만 사용할 수 있기 때문에 효율적이지 않습니다 (b / c 다음은 도시와 쿼리에서 사용되지 않습니다).
JaredC 2016 년

6

다른 모든 대답은 잘못되었습니다.

주문을 선택할 때 복합 인덱스에서 개별 열의 선택성은 중요 하지 않습니다 .

간단한 사고 과정은 다음과 같습니다. 사실상, 색인은 관련된 열의 연결입니다.

그 이론적 근거를 제공하는 유일한 차이점은 문자열에서 이전과 나중에 다른 두 개의 '문자열'을 비교하는 것입니다. 이것은 총 비용의 작은 부분입니다. 하나의 답변에서 언급했듯이 "첫 번째 패스 / 두 번째 패스"는 없습니다.

그렇다면 어떤 순서를 사용해야합니까?

  1. 테스트 열 (들)을 시작 =으로, 어떤 순서.
  2. 그런 다음 하나의 범위 열을 고정하십시오.

예를 들어, 매우 낮은 선택도 열이 있어야 이 먼저 와서 :

WHERE deleted = 0  AND  the_datetime > NOW() - INTERVAL 7 DAY
INDEX(deleted, the_datetime)

색인에서 순서를 바꾸면 완전히 무시 deleted됩니다.

(열 순서를 정하기위한 규칙이 훨씬 더 많습니다.)


내가 틀 렸기 때문에 부정적인 투표입니까? 아니면 내가 강한 의견을 가지고 있기 때문에? 또는 다른 것?
Rick James

내 downvote는 아니지만 삭제 된 = 0은 선택성이 낮지 않은 것처럼 들립니다. 테이블의 행 대부분이 될 것이라고 생각합니다.
그렉

@Greg- "낮은 선택성"을 의미한다고 생각합니다. 즉, 사용 deleted하면 원하지 않는 행을 필터링하는 데 큰 도움이되지 않습니다. 더 좋은 예가 있습니까? (이것이 내가 답을 쓸 때 내 마음에
Rick James

내 오해
그렉

1
@ClickOk-감사합니다. 내 요리 책은 몇 가지 기본 정보를 제공합니다. mysql.rjweb.org/doc.php/index_cookbook_mysql
Rick James
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.