Thứ tự của các cột trong chỉ mục PK có quan trọng không?


33

Tôi có một vài bảng rất lớn với cùng một cấu trúc cơ bản. Mỗi người có một RowNumber (bigint)DataDate (date)cột. Dữ liệu được tải bằng SQLBulkImport mỗi đêm và không có dữ liệu "mới" nào được tải - đó là bản ghi lịch sử (Tiêu chuẩn SQL, không phải Doanh nghiệp, do đó không phân vùng).

Bởi vì mỗi bit dữ liệu cần được gắn lại với các hệ thống khác và mỗi RowNumber/DataDatekết hợp là duy nhất, đó là Khóa chính của tôi.

Tôi nhận thấy rằng do cách tôi xác định PK trong Trình thiết kế bảng SSMS, RowNumberđược liệt kê đầu tiên và DataDatethứ hai.

Tôi cũng nhận thấy rằng sự phân mảnh của tôi luôn RẤT cao ~ 99%.

Bây giờ, vì mỗi lần DataDatechỉ xuất hiện một lần, tôi sẽ mong người lập chỉ mục thêm vào các trang mỗi ngày, nhưng tôi tự hỏi liệu nó có thực sự được lập chỉ mục dựa trên RowNumberđầu tiên không, và do đó phải thay đổi mọi thứ khác?


Rownumberkhông phải là một cột danh tính, đó là một int được tạo bởi một hệ thống bên ngoài (thật đáng buồn). Nó đặt lại vào đầu mỗi DataDate.

Dữ liệu mẫu

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 
...

Dữ liệu đang được tải theo RowNumberthứ tự, DataDatemỗi lần tải.

Quá trình nhập là bcp - Tôi đã thử tải vào bảng tạm thời và sau đó chọn theo thứ tự từ đó ( ORDER BY RowNumber, DataDate) nhưng vẫn xuất hiện phân mảnh cao.

Câu trả lời:


50

Thứ tự của các cột trong chỉ mục PK có quan trọng không?

Có nó làm.

Theo mặc định, ràng buộc khóa chính được thi hành trong SQL Server bởi một chỉ mục được nhóm duy nhất. Chỉ mục cụm xác định thứ tự logic của các hàng trong bảng. Có thể có một số trang chỉ mục bổ sung được thêm vào để thể hiện các mức cao hơn của chỉ mục cây b, nhưng mức (lá) thấp nhất của chỉ mục được nhóm đơn giản là thứ tự logic của chính dữ liệu.

Để rõ ràng về nó, các hàng trên một trang không nhất thiết phải được lưu trữ vật lý theo thứ tự khóa chỉ mục được nhóm. Có một cấu trúc gián tiếp riêng biệt trong trang lưu trữ một con trỏ tới mỗi hàng. Cấu trúc này được sắp xếp theo các khóa chỉ mục cụm. Ngoài ra, mỗi trang có một con trỏ đến trang trước và trang tiếp theo ở cùng cấp theo thứ tự khóa chỉ mục được nhóm.

Với khóa chính được nhóm (RowNumber, DataDate), các hàng được sắp xếp một cách hợp lý trước RowNumbervà sau đó DataDate- vì vậy tất cả các hàng RowNumber = 1được nhóm hợp lý với nhau, sau đó các hàng ở đâu RowNumber = 2và cứ thế.

Khi bạn thêm dữ liệu mới (có RowNumberstừ 1 đến n), các hàng mới thuộc về các trang hiện có một cách hợp lý, do đó, SQL Server có thể sẽ phải thực hiện rất nhiều công việc tách trang để tạo khoảng trống. Tất cả hoạt động này tạo ra rất nhiều công việc phụ (bao gồm ghi nhật ký các thay đổi) mà không thu được lợi nhuận.

Các trang phân tách cũng bắt đầu trống khoảng 50%, do đó việc chia quá nhiều có thể dẫn đến mật độ trang thấp (ít hàng hơn tối ưu trên mỗi trang). Đây không chỉ là tin xấu cho việc đọc từ đĩa (mật độ thấp hơn = nhiều trang hơn để đọc), các trang có mật độ thấp hơn cũng chiếm nhiều chỗ hơn trong bộ nhớ khi được lưu trữ.

Thay đổi chỉ mục được phân cụm thành (DataDate, RowNumber) có nghĩa là dữ liệu mới (có lẽ, cao DataDateshơn mức được lưu trữ hiện tại) được gắn vào phần cuối logic của chỉ mục được phân cụm trên các trang mới. Điều này sẽ loại bỏ các chi phí không cần thiết của việc chia trang và dẫn đến thời gian tải nhanh hơn. Dữ liệu bị phân mảnh ít hơn cũng có nghĩa là hoạt động đọc trước (đọc các trang từ đĩa ngay trước khi chúng cần cho truy vấn đang thực hiện) có thể hiệu quả hơn.

Nếu không có gì khác, các truy vấn của bạn có nhiều khả năng tìm kiếm DataDatehơn RowNumber. Một chỉ mục được nhóm trên (DataDate, RowNumber) hỗ trợ tìm kiếm chỉ mục trên DataDate(và sau đó RowNumber). Sự sắp xếp hiện tại chỉ hỗ trợ tìm kiếm trên RowNumber(và chỉ sau đó, có lẽ, trên DataDate). Bạn cũng có thể bỏ chỉ mục không bao gồm hiện tại vào DataDatekhi khóa chính được thay đổi. Chỉ mục được nhóm sẽ rộng hơn chỉ mục không bao gồm nó thay thế, vì vậy bạn nên kiểm tra để đảm bảo rằng hiệu suất vẫn được chấp nhận.

Khi nhập dữ liệu mới với bcp, bạn có thể có hiệu suất cao hơn nếu dữ liệu trong tệp nhập được sắp xếp theo các khóa chỉ mục được nhóm (lý tưởng (DataDate, RowNumber)) và bạn chỉ định bcptùy chọn:

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

Để có hiệu suất tải dữ liệu tốt nhất, bạn có thể cố gắng đạt được các bản ghi được ghi lại tối thiểu. Để biết thêm thông tin, xem:


4
Một câu trả lời tuyệt vời - Bây giờ tôi biết tôi nên làm gì VÀ tại sao. Tôi đã nghĩ như vậy, nhưng không BIẾT như vậy! Cảm ơn bạn.
BlueChippy

Mất một LOOOOONG trong khi đưa DB vào Máy chủ SQL cục bộ của tôi để kiểm tra: Trước khi thay đổi tải chỉ mục mất 45 phút ... sau đó, chỉ mất 5 !!!
BlueChippy

13

Vâng, thứ tự là rất quan trọng. Tôi rất nghi ngờ bạn từng truy vấn bởi RowNumber (ví dụ WHERE RowNumber=1). Chuỗi thời gian áp đảo được truy vấn theo ngày ( WHERE DataDate BEWEEN @start AND @end) và các truy vấn như vậy sẽ yêu cầu một tổ chức theo cụm DataDate.

Sự phân mảnh nói chung là một cá trích đỏ. Giảm phân mảnh không phải là mục tiêu của bạn ở đây, nhưng có một tổ chức phù hợp cho các truy vấn của bạn nên. Ngoài ra, việc giảm phân mảnh cũng là một suy nghĩ tốt để có, nhưng không phải là một mục tiêu riêng. Nếu bạn có một mô hình dữ liệu được sắp xếp hợp lý phù hợp với khối lượng công việc của bạn (các truy vấn của bạn được bảo vệ đúng cách) bạn có các phép đo cho thấy sự phân mảnh là hiệu suất ảnh hưởng thì chúng ta có thể nói về nó.


Tôi cũng có một (các) chỉ mục không được nhóm trên DataDate, như bạn nói thường là WHEREmệnh đề trong các truy vấn.
BlueChippy

1
Nếu ĐẶT HÀNG của các cột là quan trọng, liệu tác động của thứ tự incorrecrt có làm tăng I / O của tôi không? Tôi nghĩ rằng đó là thứ tự của RowNumber và do đó phải thực hiện rất nhiều công việc về các chỉ mục mỗi lần, trong khi nó phải dựa trên DataDate?
BlueChippy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.