클러스터형 인덱스와 비 클러스터형 인덱스의 성능 차이


22

나는 읽고 있었다 Clustered그리고 Non Clustered Indexes.

Clustered Index-데이터 페이지를 포함합니다. 이는 전체 행 정보가 클러스터형 인덱스 열에 있음을 의미합니다.

Non Clustered Index-클러스터 색인 열 (사용 가능한 경우) 또는 파일 식별자 + 페이지 번호 + 페이지의 총 행 형식의 행 로케이터 정보 만 포함합니다. 이는 실제 데이터를 찾기 위해 쿼리 엔진이 추가 단계를 수행해야 함을 의미합니다.

쿼리 - 우리는 테이블 하나를 가질 수 있음을 알고 나는 실제적인 예를의 도움으로 성능 차이를 확인할 수있는 방법 Clustered Index및 제공 sorting상기 Clustered Index Column와 Non Clustered Index제공하지 않는 sorting999 지원할 수 Non Clustered Indexes있는 SQL Server 2008과 249 SQL Server 2005.


2
무엇을 할 때의 성능 차이, 그 테이블로 어떤 종류의 작업을 원하십니까?, 모든 요구에 맞는 단일 솔루션은 없습니다
— Lamak

2
아마도 여기서 실질적인 논의가있을 것입니다. stackoverflow.com/questions/91688/… stackoverflow.com/questions/5070529/… stackoverflow.com/questions/1251636/… 클러스터 된 인덱스와 비 클러스터형 인덱스의 차이점에 대한 논문을 작성할 수 있지만 우리는 생각하지 않습니다 아직 읽을 수없는 내용은 말할 것입니다.
— Aaron Bertrand

4
"이것은 쿼리 엔진이 실제 데이터를 찾기 위해 추가 단계를 수행해야 함을 의미합니다." 당신이 열하는 필요한 모든 경우에 실제로 인덱스에 포함을 , 당신은 하지 않습니다 당신이 클러스터되지 않은 인덱스에서 대상 행을 찾은 후 추가 단계를 수행해야합니다. 비 클러스터형 인덱스에서 다루지 않는 열이 필요한 경우에만 SQL Server에서 책갈피 조회 를 수행해야합니다 .
— Nick Chammas

답변:


43

그런 중요한 개념이므로 매우 좋은 질문입니다. 이것은 큰 주제이며 내가 보여 드리려는 것은 단순화되어 기본 개념을 이해할 수 있습니다.

먼저 클러스터 된 인덱스 싱크 테이블이 표시 됩니다. SQL Server에서 테이블에 클러스터형 인덱스가 없으면 힙입니다. 테이블에서 클러스터형 인덱스를 만들면 실제로 테이블이 b- 트리 형식 구조로 변환됩니다. 클러스터형 인덱스는 테이블이며 테이블과 분리되지 않습니다.

왜 클러스터형 인덱스를 하나만 가질 수 있는지 궁금한 적이 있습니까? 클러스터 된 인덱스가 두 개인 경우 테이블 사본이 두 개 필요합니다. 그것은 결국 데이터를 포함합니다.

간단한 예제를 사용하여이를 설명하려고합니다.

참고 : 이 예제에서 테이블을 만들고 3 백만 이상의 임의 항목으로 채 웠습니다. 그런 다음 실제 쿼리를 실행하고 실행 계획을 여기에 붙여 넣습니다.

실제로 파악해야 할 것은 O 표기법 또는 운영 효율성 입니다. 다음 표가 있다고 가정 해 봅시다.

CREATE TABLE [dbo].[Customer](
[CustomerID] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [varchar](100) NOT NULL,
[CustomerSurname] [varchar](100) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED 
(
[CustomerID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF
  , IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS  = ON
  , ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

여기 CustomerID에 클러스터 키가있는 기본 테이블이 있습니다 (기본 키는 기본적으로 클러스터 됨). 따라서 테이블은 기본 키 CustomerID를 기준으로 정렬 / 정렬됩니다. 중간 수준에는 CustomerID 값이 포함됩니다. 데이터 페이지는 전체 행을 포함하므로 테이블 행입니다.

또한 CustomerName 필드에 비 클러스터형 인덱스를 만듭니다. 다음 코드가이를 수행합니다.

CREATE NONCLUSTERED INDEX [ix_Customer_CustomerName] ON [dbo].[Customer] 
 (
[CustomerName] ASC
 )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF
  , SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF
  , DROP_EXISTING = OFF, ONLINE = OFF
  , ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

따라서이 색인에서는 데이터 페이지 / 리프 레벨 노드에서 클러스터 된 색인의 중간 레벨에 대한 포인터를 찾을 수 있습니다. 색인은 CustomerName 필드를 기준으로 정렬 / 정렬됩니다. 따라서 중간 수준에는 CustomerName 값이 포함되고 리프 수준에는 포인터가 포함됩니다 (이 포인터 값은 실제로 기본 키 값 또는 CustomerID 열임).

다음 쿼리를 실행하면 좋습니다.

SELECT * FROM Customer WHERE CustomerID = 1 

SQL은 검색 조작을 통해 클러스터형 인덱스를 읽습니다. 시크 작업은 순차적 검색 인 스캔보다 훨씬 효율적인 이진 검색입니다. 위의 예에서 인덱스를 읽고 이진 검색을 사용하면 SQL이 찾고있는 기준과 일치하지 않는 데이터를 제거 할 수 있습니다. 쿼리 계획에 대해서는 첨부 된 스크린 샷을 참조하십시오.

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

따라서 찾기 작업에 대한 작업 수 또는 O 표기법은 다음과 같습니다.

  1. 검색된 값을 중간 수준의 값과 비교하여 클러스터형 인덱스에서 이진 검색을 수행하십시오.
  2. 일치하는 값을 반환하십시오 (클러스터형 인덱스에는 모든 데이터가 있으므로 행 데이터이므로 인덱스의 모든 열을 반환 할 수 있음을 기억하십시오)

두 가지 작업입니다. 그러나 다음 쿼리를 실행하면

SELECT * FROM Customer WHERE CustomerName ='John'

SQL은 이제 CustomerName에서 클러스터되지 않은 인덱스를 사용하여 검색을 수행합니다. 그러나 이것이 비 클러스터형 인덱스이므로 행의 모든 ​​데이터를 포함하지는 않습니다.

따라서 SQL은 중간 수준에서 검색하여 일치하는 레코드를 찾은 다음 반환 된 값을 사용하여 조회하여 클러스터형 인덱스 (일명 테이블)에서 다른 데이터를 검색하여 실제 데이터를 검색합니다. 이것은 내가 알고 있지만 혼란스럽게 들리지만 계속 읽으면 모든 것이 분명해질 것입니다.

비 클러스터형 인덱스에는 CustomerName 필드 (중간 노드에 저장된 인덱스 필드 값)와 CustomerID 인 데이터에 대한 포인터 만 포함되므로 인덱스에는 CustomerSurname 레코드가 없습니다. CustomerSurname은 클러스터형 인덱스 또는 테이블에서 가져와야합니다.

이 쿼리를 실행할 때 다음 실행 계획이 나타납니다.

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

위의 스크린 샷에서 주목해야 할 두 가지 사항이 있습니다.

  1. SQL에서 누락 된 색인 (녹색 텍스트)이 있다고 말합니다. SQL은 CustomerID와 CustomerSurname을 포함하는 CustomerName에 인덱스를 만들 것을 제안합니다.
  2. 또한 쿼리 시간의 99 %가 기본 키 인덱스 / 클러스터형 인덱스에서 키 조회를 수행하는 데 소비되는 것을 볼 수 있습니다.

SQL이 CustomerName의 인덱스를 다시 제안하는 이유는 무엇입니까? 인덱스에는 CustomerID 만 포함되어 있고 CustomerName SQL은 여전히 ​​테이블 / 클러스터형 인덱스에서 CustomerSurname을 찾아야합니다.

인덱스를 생성하고 인덱스에 CustomerSurname 열을 포함 시키면 비 클러스터형 인덱스를 읽음으로써 전체 쿼리를 만족시킬 수 있습니다. 이것이 SQL이 클러스터되지 않은 인덱스를 변경하도록 제안하는 이유입니다.

여기서 클러스터 된 키에서 CustomerSurname 열을 가져 오기 위해 SQL이 수행해야하는 추가 작업을 볼 수 있습니다.

따라서 작업 수는 다음과 같습니다.

  1. 검색된 값을 중간 수준의 값과 비교하여 비 클러스터형 인덱스에서 이진 검색
  2. 일치하는 노드의 경우 클러스터 된 인덱스의 데이터에 대한 포인터를 포함 할 리프 레벨 노드를 읽으십시오 (리프 레벨 노드에는 기본 키 값이 포함됨).
  3. 반환 된 각 값에 대해 클러스터 된 인덱스 (테이블)를 읽어 행 값을 가져옵니다. 여기서 CustomerSurname을 읽습니다.
  4. 일치하는 행 반환

그것은 값을 얻는 4 가지 작업입니다. 클러스터형 인덱스 읽기와 비교하여 필요한 작업 양이 두 배입니다. 클러스터형 인덱스는 모든 데이터를 포함하므로 가장 강력한 인덱스임을 보여줍니다.

마지막 요점을 명확히하기 위해서입니다. 비 클러스터형 인덱스의 포인터가 기본 키 값이라고 말하는 이유는 무엇입니까? 비 클러스터형 인덱스의 리프 수준 노드에는 쿼리를 다음과 같이 변경하는 기본 키 값이 포함되어 있음을 보여줍니다.

SELECT CustomerID
FROM Customer
WHERE CustomerName='Jane'

이 쿼리에서 SQL은 비 클러스터형 인덱스에서 CustomerID를 읽을 수 있습니다. 클러스터형 인덱스를 조회 할 필요는 없습니다. 이것은 다음과 같은 실행 계획으로 볼 수 있습니다.

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

이 쿼리와 이전 쿼리의 차이점을 확인하십시오. 조회가 없습니다. SQL은 비 클러스터형 인덱스에서 모든 데이터를 찾을 수 있습니다

클러스터 된 인덱스가 테이블이고 비 클러스터형 인덱스가 모든 데이터를 포함하지 않는다는 것을 이해하기 시작할 수 있기를 바랍니다. 이진 검색을 수행 할 수 있지만 클러스터형 인덱스 만 모든 데이터를 포함하기 때문에 인덱싱은 선택 속도를 높입니다. 따라서 비 클러스터형 인덱스를 검색하면 거의 항상 클러스터형 인덱스에서 값이로드됩니다. 이러한 추가 작업으로 클러스터되지 않은 인덱스는 클러스터 된 인덱스보다 효율성이 떨어집니다.

이것이 문제를 해결하기를 바랍니다. 이해가되지 않는 부분이 있으면 의견을 적어주십시오. 여기가 다소 늦었고 뇌가 약간 평평 해졌습니다. 붉은 황소를위한 시간.


질문이 있습니다. WHy는이 쿼리 SELECT * FROM Customer WHERE CustomerName = 'John'에 대한 CustomerName의 비 클러스터형 인덱스에서 인덱스를 검색하는 검색입니다. 비 클러스터형 인덱스이므로 customername이 정렬되지 않습니다. 따라서 인덱스 스캔을 수행해서는 안됩니다.
— ckv

BTW 위의 질문을 제외하고는 큰 대답을 완전히 이해했습니다.
— ckv

1
색인은 데이터 순서대로 정렬됩니다. 예를 들어 인덱싱 된 값이므로 고객 이름으로 정렬됩니다. 따라서 정렬됩니다. 여전히 리프 레벨 또는 페이지를 스캔해야합니다.
— Namphibian

9

"이는 실제 데이터를 찾기 위해 쿼리 엔진이 추가 단계를 수행해야 함을 의미합니다."

반드시 그럴 필요는 없습니다. 인덱스가 주어진 쿼리를 다루는 경우 데이터 페이지로 이동할 필요가 없습니다. 또한 포함 된 열을 사용하면 비 클러스터형 인덱스에 추가 열을 추가하여 키 크기를 변경하지 않고도 덮어 쓸 수 있습니다.

따라서 궁극적 인 대답은 다음과 같습니다. (단 하나의 질문으로 실제로 다룰 수있는 것보다 훨씬 많은 정보에 의존합니다)-인덱스의 모든 기능을 이해해야하며 주어진 쿼리에 대한 실행 계획이 예상과 다를 수 있습니다.

내가 가진 일반적인 규칙은 테이블에 항상 클러스터 된 인덱스 (및 일반적으로 ID 또는 순차 GUID)가 있지만 성능을 위해 비 클러스터형 인덱스가 추가된다는 것입니다. 그러나 항상 예외가 있습니다. 힙 테이블에는 장소가 있고 더 넓은 클러스터 된 인덱스에는 장소가 있습니다. 페이지 당 더 많은 행에 맞게 더 좁아 보이는 중복 인덱스가 있습니다. 등

그리고 나는 허용되는 다양한 색인의 한계에 대해 걱정하지 않을 것입니다. 많은 실제 사례에서 거의 작동하지 않을 것입니다.


2
+1- there are always exceptions너무 많은 사람들이 이것을 생략하고 모든 클러스터형 인덱스가 int identity무엇이든 상관 없다고 생각합니다.
— JNK
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.