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.