PK 인덱스의 열 순서가 중요합니까?


33

동일한 기본 구조를 가진 매우 큰 테이블이 몇 개 있습니다. 각각 하나 RowNumber (bigint)와 DataDate (date)열이 있습니다. 매일 밤 SQLBulkImport를 사용하여 데이터를로드하고 기록 데이터 (Enterprise가 아닌 SQL 표준이므로 분할 없음)의 "새"데이터가로드되지 않습니다.

각 데이터 비트를 다른 시스템에 다시 연결해야하고 각 RowNumber/DataDate조합이 고유하기 때문에 이것이 기본 키입니다.

SSMS Table Designer에서 PK를 정의한 방식으로 인해 RowNumber첫 번째와 DataDate두 번째 로 나열됩니다 .

또한 조각화가 항상 ~ 99 %로 매우 높다는 것을 알았습니다.

이제 각 항목 DataDate이 한 번만 나타나 므로 인덱서가 매일 페이지에 추가 할 것으로 예상하지만 실제로는 RowNumber첫 페이지를 기준으로 색인을 생성 하고 다른 모든 것을 이동해야하는지 궁금합니다 .


RownumberID 열이 아니며 외부 시스템 (슬프게도)에 의해 생성 된 int입니다. 각 시작시 재설정됩니다 DataDate.

데이터 예

RowNumber | DataDate | a | b | c..... 
   1      |2013-08-01| x | y | z 
   2      |2013-08-01| x | y | z 
...
   1      |2013-08-02| x | y | z 
   2      |2013-08-02| x | y | z 
...

데이터는로드 당 RowNumber하나씩 순서대로 DataDate로드됩니다.

가져 오기 프로세스는 bcp입니다-임시 테이블에로드 한 다음 순서대로 선택 ORDER BY RowNumber, DataDate했지만 여전히 높은 조각화가 발생합니다.

답변:


50

PK 인덱스의 열 순서가 중요합니까?

그렇습니다.

기본적으로 기본 키 제약 조건은 고유 클러스터형 인덱스에 의해 SQL Server에서 적용됩니다. 클러스터형 인덱스는 테이블에서 행 의 논리적 순서를 정의 합니다. b- 트리 인덱스의 상위 수준을 나타 내기 위해 추가 된 추가 인덱스 페이지가 여러 개있을 수 있지만 클러스터형 인덱스의 최저 (리프) 수준은 단순히 데이터 자체의 논리적 순서입니다.

이를 명확하게하기 위해 페이지의 행이 반드시 실제로 클러스터 된 인덱스 키 순서로 저장되는 것은 아닙니다 . 페이지 내에 각 행에 대한 포인터를 저장하는 별도의 간접 구조가 있습니다. 이 구조는 클러스터 된 인덱스 키로 정렬됩니다. 또한 각 페이지에는 클러스터 된 인덱스 키 순서에서 같은 수준으로 이전 페이지와 다음 페이지에 대한 포인터가 있습니다.

의 클러스터 된 기본 키를 사용 (RowNumber, DataDate)하면 행이 먼저 논리적으로 정렬 된 RowNumber다음 논리적으로 그룹화 된 DataDate모든 행 , 그런 다음 행 등이 논리적으로 정렬 됩니다.RowNumber = 1RowNumber = 2

새 데이터를 추가 할 때 ( RowNumbers1에서 n까지) 새 행은 기존 페이지 내에 논리적으로 속하므로 SQL Server는 공간을 만들기 위해 페이지를 분할하는 많은 작업을 수행해야 할 것입니다. 이 모든 활동은 아무런 변화없이 많은 추가 작업 (변경 사항 기록 포함)을 생성합니다.

분할 페이지도 약 50 % 비어 있기 때문에 페이지를 너무 많이 분할하면 페이지 밀도가 낮아질 수 있습니다 (페이지 당 최적 행보다 적은 행 수). 이 나쁜 소식은 디스크에서 읽을 때 (낮은 밀도 = 더 많은 페이지를 읽을 때)뿐만 아니라 캐시 될 때 메모리에서 더 많은 공간을 차지합니다.

클러스터형 인덱스를로 변경하면 (DataDate, RowNumber새로운 데이터 (아마도 DataDates현재 저장된 것보다 높은 데이터 )가 새로운 페이지의 클러스터형 인덱스의 논리적 끝에 추가됩니다. 이렇게하면 분할 페이지의 불필요한 오버 헤드가 제거되고로드 시간이 더 빨라집니다. 조각화되지 않은 데이터는 또한 미리 읽기 작업 (진행중인 쿼리에 필요하기 직전에 디스크에서 페이지 읽기)이 더 효율적일 수 있음을 의미합니다.

다른 것이 없으면 검색어 DataDate보다 검색 가능성이 훨씬 높습니다 RowNumber. 클러스터형 인덱스 on (DataDate, RowNumber)은 인덱스 검색을 지원합니다 DataDate(및 RowNumber). 기존 배열은 탐색 만 지원 RowNumber하고 그 후에 만 지원합니다 DataDate. DataDate기본 키가 변경되면 기존의 비 클러스터형 인덱스를 삭제할 수 있습니다 . 클러스터형 인덱스는 대체 된 비 클러스터형 인덱스보다 넓으므로 성능이 허용 가능한지 테스트해야합니다.

로 새 데이터를 가져올 때 가져 bcp오기 파일 내의 데이터가 클러스터 된 인덱스 키 (이상적으로 (DataDate, RowNumber) 로 정렬되고 bcp옵션 을 지정하면 성능이 향상 될 수 있습니다.

-h "ORDER(DataDate,RowNumber), TABLOCK"

최상의 데이터 로딩 성능을 위해 최소한의 로그 삽입을 달성하려고 시도 할 수 있습니다. 자세한 내용은 다음을 참조하십시오.


4
훌륭한 답변-이제 내가해야 할 일과 이유를 알고 있습니다. 나는 그렇게 생각했지만 알지 못했습니다! 고맙습니다.
— BlueChippy

테스트를 위해 DB를 로컬 SQL Server로 가져 오는 동안 LOOOOONG을 가져 왔습니다. 인덱스로드를 변경하기 전에 45 분이 걸렸습니다 ... 후 5 분이 걸렸습니다!
— BlueChippy

13

예, 순서가 중요합니다. RowNumber (예 :)로 쿼리 한 적이 있습니다 WHERE RowNumber=1. 압도적으로 시계열은 날짜 ( WHERE DataDate BEWEEN @start AND @end) 별로 쿼리되며 이러한 쿼리는에 의해 클러스터 된 조직이 필요합니다 DataDate.

일반적으로 조각화는 붉은 청어입니다. 여기서 조각화를 줄이는 것이 목표가 아니라 쿼리에 적합한 조직을 갖추어야합니다. 조각화를 줄이는 것도 좋은 생각이지만 그 자체로는 목표가 아닙니다. 당신이 당신의 작업 부하에 맞는 제대로 조직 된 데이터 모델이있는 경우 (쿼리가 제대로 적용됩니다) 그리고 당신이 우리가 그것에 대해 이야기 할 수 성능에 영향을주지 단편화를 보여 측정이있다.


또한 DataDate에는 비 클러스터형 인덱스가 WHERE있으며 쿼리에서 종종 절입니다.
— BlueChippy

1
열의 순서가 중요하면 잘못된 순서의 영향으로 인해 I / O가 증가합니까? 내 생각은 RowNumber에 의해 정렬되므로 매번 인덱스에 대해 많은 작업을 수행해야하지만 DataDate를 기반으로해야한다는 것입니다.
— BlueChippy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.