Khi nào một khóa chính được khai báo không được nhóm?


169

Trong khi tạo cơ sở dữ liệu kiểm tra cho một câu hỏi khác mà tôi đã hỏi trước đó, tôi đã nhớ về Khóa chính có thể được khai báo NONCLUSTERED

Khi nào bạn sẽ sử dụng NONCLUSTEREDkhóa chính thay vì CLUSTEREDkhóa chính?

Cảm ơn trước

Câu trả lời:


187

Câu hỏi không phải là 'khi nào PK nên là NC', mà thay vào đó bạn nên hỏi 'khóa thích hợp cho chỉ mục được nhóm' là gì?

Và câu trả lời thực sự phụ thuộc vào cách bạn truy vấn dữ liệu . Chỉ mục được nhóm có một lợi thế so với tất cả các chỉ mục khác: vì nó luôn bao gồm tất cả các cột, luôn luôn bao phủ. Do đó, các truy vấn có thể tận dụng chỉ mục được phân cụm chắc chắn không cần sử dụng tra cứu để đáp ứng một số cột và / hoặc các vị từ được chiếu.

Một mảnh khác của câu đố là làm thế nào một chỉ mục có thể được sử dụng ? Có ba mẫu điển hình:

  • thăm dò, khi một giá trị khóa duy nhất được tìm kiếm trong chỉ mục
  • quét phạm vi, khi một phạm vi các giá trị chính được lấy
  • sắp xếp theo yêu cầu, khi một chỉ mục có thể đáp ứng một đơn đặt hàng bằng cách yêu cầu sắp xếp dừng và đi

Vì vậy, nếu bạn phân tích tải dự kiến ​​của mình (các truy vấn) và phát hiện ra rằng một số lượng lớn các truy vấn sẽ sử dụng một chỉ mục cụ thể vì chúng sử dụng một mẫu truy cập nhất định có lợi từ một chỉ mục, sẽ hợp lý khi đề xuất chỉ mục đó làm chỉ mục được nhóm.

Tuy nhiên, một yếu tố khác là khóa chỉ mục được phân cụm là khóa tra cứu được sử dụng bởi tất cả các chỉ mục không được phân cụm và do đó, một khóa chỉ mục được phân cụm rộng tạo ra hiệu ứng gợn và mở rộng tất cả các chỉ mục không phân cụm và các chỉ mục rộng có nghĩa là nhiều trang hơn, nhiều I / O hơn , trí nhớ nhiều hơn, lòng tốt ít hơn.

Một chỉ mục cụm tốt là ổn định , nó không thay đổi trong suốt vòng đời của thực thể, bởi vì một thay đổi trong các giá trị khóa chỉ mục được phân cụm có nghĩa là hàng phải được xóa và chèn trở lại.

Và một chỉ mục được nhóm tốt phát triển theo thứ tự không ngẫu nhiên (mỗi giá trị khóa mới được chèn lớn hơn giá trị trước) để tránh phân tách và phân mảnh trang (mà không làm rối tung FILLFACTORs).

Vì vậy, bây giờ chúng ta đã biết khóa chỉ mục được nhóm tốt là gì, khóa chính (là thuộc tính logic mô hình hóa dữ liệu) có khớp với các yêu cầu không? Nếu có, thì PK nên được nhóm lại. Nếu không, thì PK không được phân cụm.

Để đưa ra một ví dụ, hãy xem xét một bảng sự kiện bán hàng. Mỗi mục có một ID là khóa chính. Nhưng phần lớn các truy vấn yêu cầu dữ liệu giữa một ngày và một ngày khác, do đó, khóa chỉ mục được nhóm tốt nhất sẽ là ngày bán hàng , không phải ID . Một ví dụ khác về việc có một chỉ mục được nhóm khác với khóa chính là khóa có độ chọn lọc rất thấp, như 'danh mục' hoặc 'trạng thái', một khóa chỉ có rất ít giá trị riêng biệt. Có một khóa chỉ mục được nhóm với khóa chọn thấp này là khóa ngoài cùng bên trái, ví dụ (state, id), thường có ý nghĩa vì các phạm vi quét tìm kiếm tất cả các mục trong một 'trạng thái' cụ thể.

Một lưu ý cuối cùng về khả năng của khóa chính không được phân cụm trong một heap (tức là không có chỉ số nào được phân cụm). Đây có thể là một kịch bản hợp lệ, lý do điển hình là khi hiệu suất chèn số lượng lớn là rất quan trọng, vì các đống có thông lượng chèn hàng loạt tốt hơn đáng kể khi so sánh với các chỉ số được nhóm.


1
"Sắp xếp theo yêu cầu, khi một chỉ mục có thể đáp ứng một đơn đặt hàng bằng cách yêu cầu sắp xếp dừng và đi" nghĩa là gì ở đây?
Mike Sherrill 'Nhớ lại mèo'

2
@RemusRusanu. +1 Câu trả lời rất hữu ích. Một câu hỏi liên quan đến ví dụ (state, id). Trong ví dụ này, yêu cầu "chỉ số phân cụm tốt phát triển theo thứ tự không ngẫu nhiên" sẽ không được đáp ứng, phải không? Vì vậy, chúng ta có thể coi nó là chỉ số cụm tốt?
Lijo

26

Lý do cơ bản để sử dụng các chỉ mục Clustered được nêu trên Wikipedia :

Phân cụm làm thay đổi khối dữ liệu thành một thứ tự riêng biệt nhất định để khớp với chỉ mục, dẫn đến dữ liệu hàng được lưu trữ theo thứ tự. Do đó, chỉ có thể tạo một chỉ mục cụm trên một bảng cơ sở dữ liệu nhất định. Chỉ số nhóm có thể làm tăng đáng kể tốc độ tổng thể thu hồi, nhưng thường chỉ nơi dữ liệu được truy cập tuần tự trong cùng hoặc đảo ngược thứ tự của các nhóm chỉ số , hoặc khi một loạt các mặt hàng được chọn.

Nói rằng tôi có một bảng Người và những người này có cột Quốc gia và Khóa chính duy nhất. Đó là bảng nhân khẩu học, vì vậy đây là những điều duy nhất tôi quan tâm; Quốc gia nào và có bao nhiêu người độc đáo gắn liền với quốc gia đó.

Do đó, tôi chỉ có khả năng CHỌN Ở ĐÂU hoặc ĐẶT HÀNG theo cột Quốc gia; một chỉ mục được nhóm trên Khóa chính không giúp ích gì cho tôi, tôi không truy cập dữ liệu này bằng PK, tôi đang truy cập nó bằng cột khác. Vì tôi chỉ có thể có một chỉ mục được nhóm trên một bảng, việc khai báo PK của tôi là Clustered sẽ ngăn tôi sử dụng Chỉ mục cụm trên Quốc gia.

Ngoài ra, đây là một bài viết hay về Chỉ mục được phân cụm so với các chỉ mục không bao gồm , hóa ra các chỉ mục được phân cụm gây ra các vấn đề về hiệu năng chèn trong SQL Server 6.5 (ít nhất hy vọng là không phù hợp với hầu hết chúng ta ở đây).

Nếu bạn đặt một chỉ mục được nhóm trên một cột IDENTITY, thì tất cả các lần chèn của bạn sẽ diễn ra trên trang cuối cùng của bảng - và trang đó bị khóa trong thời gian của mỗi IDENTITY. Không có vấn đề gì lớn ... trừ khi bạn có 5000 người mà tất cả đều muốn trang cuối cùng. Sau đó, bạn có rất nhiều tranh cãi cho trang đó

Lưu ý rằng đây không phải là trường hợp trong các phiên bản sau.


3
FIY, bạn đã đề cập đến SQL Server 6.5: dba.stackexchange.com/questions/1584/iêu
gbn

15

Nếu khóa chính của bạn là của UNIQUEIDENTIFIER, hãy đảm bảo chỉ định nó NONCLUSTERED. Nếu bạn làm cho nó được nhóm lại, mỗi lần chèn sẽ phải thực hiện một loạt các bản ghi để chèn hàng mới vào đúng vị trí. Điều này sẽ tăng hiệu suất.


1
Mặc dù tôi cố gắng tránh UUID cho các khóa cụm, tôi tin rằng lý do ở trên có thể không đầy đủ. Máy chủ SQL không nhất thiết phải cải tổ các hàng để chèn a vào đúng vị trí (nếu bạn có nghĩa là "giữa giá trị thấp hơn và cao hơn"). Hãy xem xét một chèn vào giữa một bảng hàng nghìn tỷ. Thêm sự quyết định là cần thiết, đó có thể là những gì bạn muốn nói. Một UNIQUEIDENTIFIERkiểu tuần tự cũng tồn tại và có cùng xác suất tạo các khóa duy nhất, mặc dù nó vẫn phải chịu kích thước 128.
Charles Burns

7

Một ví dụ rất phổ biến:

  • Customerbảng với CustomerIDnhưCLUSTERED PRIMARY KEY
  • Bảng đặt hàng với OrderID (PK), CustomerID, OrderDatevà một số cột khác
  • OrderPositions với OrderPositionID (PK), OrderId, ProductID, Amount, Price ...
  • bạn phải lập chỉ mục các bảng thứ tự

Tất nhiên "nó phụ thuộc" là - gần như luôn luôn - câu trả lời đúng, nhưng hầu hết các ứng dụng (không phải BI-Báo cáo) sẽ hoạt động dựa trên khách hàng (ví dụ: bạn đăng nhập với tư cách là khách hàng 278 vào trang web và nhấp vào "Đơn hàng của tôi" hoặc nhân viên bán hàng liệt kê tất cả các đơn đặt hàng cho khách hàng 4569 hoặc thói quen hóa đơn của bạn sẽ tổng hợp tất cả các đơn đặt hàng cho khách hàng 137).

Trong trường hợp này, sẽ không có nhiều ý nghĩa để phân cụm bảng theo OrderID. Có, bạn sẽ có các truy vấn SELECT ... WHERE OrderId = ?để liệt kê chi tiết đơn hàng, nhưng đây thường là chỉ mục ngắn và rẻ (3 lần đọc) tìm kiếm.

Mặt khác, nếu bạn phân cụm Orderbảng của mình theo CustomerID, nó sẽ không phải thực hiện nhiều lần tra cứu chính mỗi khi bạn truy vấn bảng CustomerId = ?.

Các CLUSTERED INDEXnên luôn UNIQUE, nếu không SQL Server sẽ thêm một vô hình (= không sử dụng được) INT cột UNIQUIFIERđể đảm bảo uniquiness - và nó sẽ có ý nghĩa nhiều hơn nữa để thêm sản (có thể sử dụng) dữ liệu sau đó một số nội dung ngẫu nhiên (tùy thuộc vào thứ tự chèn).

Bởi vì một khách hàng (hy vọng) sẽ đặt nhiều hơn một đơn hàng, chúng tôi sẽ phải thêm OrderIDhoặc (nếu bạn thường sắp xếp cho việc này) OrderDate(nếu đó là một datetime - nếu không thì khách hàng sẽ bị giới hạn một đơn hàng mỗi ngày) các CLUSTERED INDEXvà kết thúc với:

CREATE UNIQUE CLUSTERED INDEX IX_Orders_UQ on Orders (CustomerID, OrderID)

Các quy tắc tương tự áp dụng cho OrderPositionsbảng. Thông thường hầu hết các truy vấn sẽ liệt kê tất cả các vị trí cho vào thứ tự cụ thể, vì vậy bạn nên tạo ra PK với OrderPositionIDnhư NONCLUSTEREDvà một UNIQUE CLUSTERED INDEXtrên OrderId, OrderPositionID.

BTW: đúng là Customerbảng được phân cụm bởi PK của nó ( CustomerIDbởi vì nó là "Bảng cấp cao nhất" và sẽ - trong một ứng dụng thông thường - chủ yếu được truy vấn bởi CustomerID của nó.

Bàn tra cứu tinh khiết như ví dụ Gendershay InvoiceTypeshay PaymentTypecũng là một ví dụ về bảng nên được nhóm bởi PK của nó (vì bạn thường sẽ tham gia cùng họ trên GenderId, InvoiceTypeIdhoặc PaymentTypeId).


2

Khi một chỉ số cụm được coi là có lợi cho toàn bộ hệ thống hơn là một cụm PK bằng cách sử dụng một số phép đo hiệu suất. Chỉ có thể có một chỉ mục được nhóm trên một bảng.

Các biện pháp ví dụ về hiệu suất là thời gian truy vấn đơn (tốc độ), tích hợp tổng thời gian truy vấn so với bảng (hiệu quả) và phải thêm nhiều cột bao gồm vào một chỉ mục không phân cụm rất lớn để đạt được hiệu suất tương tự như cụm (kích thước ).

Điều này có thể xảy ra khi dữ liệu thường được truy xuất bằng cách sử dụng một chỉ mục không phải là duy nhất, chứa null (không được phép trong PK) hoặc PK được thêm vào vì lý do thứ cấp (chẳng hạn như sao chép hoặc nhận dạng hồ sơ kiểm toán).

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.