Cột nhận dạng như ý tưởng cụm sao xấu?


7

Nó chỉ là một vài tháng lập trình trong SQL Server đối với tôi vì vậy kiến ​​thức của tôi không tốt về nhiều mặt. Trong một dự án đã có sẵn tại nơi làm việc, tôi đã bắt gặp nhiều bảng với các khóa chính tổng hợp lớn với chỉ mục được nhóm. Từ những gì tôi đã thu thập được, một cột lớn / cột tổng hợp với chỉ mục được nhóm thực hiện rất hiệu quả và đôi khi giải pháp logic là một cột định danh. Nhưng đồng thời, tôi đã bắt gặp nhiều người đổ lỗi cho việc sử dụng quá mức các cột danh tính.

Nhưng tôi chưa bao giờ bắt gặp một ví dụ trong đó cột danh tính là một ý tưởng tồi.

Gần đây, chúng tôi đã tiêu chuẩn hóa rằng mỗi bảng nên có một cột định danh là chỉ mục được nhóm - cho dù chúng tôi có sử dụng nó làm PK hay không, vì chúng tôi yêu cầu nó cho một số mục đích xuất khẩu.

Vì vậy, tôi muốn một số ví dụ, trong các tình huống thực tế, trong đó sử dụng một cột danh tính làm chỉ mục cụm là một ý tưởng tồi.

Mặc dù đôi khi nó làm cho cuộc sống của chúng ta dễ dàng, tôi chưa bao giờ gặp phải một kịch bản mà nó sẽ bị coi là xấu.

PS: Tôi nghĩ rằng câu hỏi của tôi hơi ngây thơ nhưng nó đang làm phiền tôi rất nhiều vì vậy tôi đã phải hỏi về nó.


2
Đọc các bài đăng trên blog của Kimberly Tripp về chỉ mục được nhóm - cô ấy là Nữ hoàng lập chỉ mục trong thế giới Máy chủ SQL và khuyến nghị cơ bản của cô ấy là luôn sử dụng INT IDENTITYlàm khóa chính (và được nhóm) trên hầu hết mọi bảng. Đó là một đề xuất tốt thực hành tốt nhất và thường hoạt động tốt. Theo tôi, trường hợp này không phải là một ý tưởng tốt là tương đối hiếm.
marc_s

1
Vâng, tôi đã đọc những blog đó và kể từ khi làm theo thông lệ này. Nhưng tôi chỉ muốn biết các trường hợp họ sẽ tạo ra vấn đề, như được đề cập dưới đây. :)
Kai

Câu trả lời:


5

Tôi thường sử dụng một cột danh tính làm khóa chính cụm. Tuy nhiên, trong một số trường hợp (hiếm?), Điều này không lý tưởng vì LastPageInsertLatchContention. Điều này xảy ra nếu một bảng chứa đầy dữ liệu. Do khóa nhận dạng, tất cả những điều này của INSERT muốn viết trang cuối cùng của bảng (chỉ mục). Vì vậy, trang này có thể bị khóa và hiệu suất có thể tốt hơn với một giải pháp khác.

Xem

để biết chi tiết.


1
Tôi nghĩ rằng "chứa đầy dữ liệu" nên rõ ràng hơn: "có khối lượng chèn cao liên tục" là nguyên nhân trực tiếp hơn của hệ thống, không chỉ là bảng lớn.
Aaron Bertrand

Tất nhiên bạn có quyền. Đây chỉ là trường hợp khi nhiều hàng sẽ được chèn cùng một lúc. Nó không phụ thuộc vào kích thước của bảng.
Lothar Kraner

Vì vậy, tôi có một bảng trong đó chúng tôi nhập rất nhiều dữ liệu (~ 100.000 hàng ở định dạng txt) bằng cách sử dụng chèn số lượng lớn. Tôi đoán trong trường hợp này danh tính cụm chỉ số có thể tạo ra một vấn đề.
Kai

Nó phụ thuộc :). Trước hết, nó phụ thuộc vào tải của bảng cùng lúc từ các quy trình khác. Nó cũng phụ thuộc vào kích thước của các hàng của bạn (càng nhiều hàng diễn ra trong một trang 8K, vấn đề có thể xảy ra càng nhiều). Thông thường nó không phải là một vấn đề. Tôi sợ bạn phải kiểm tra nó bởi vì thay đổi điều này thường sẽ là một vấn đề lớn. Có lẽ những người khác cũng phải đối mặt với vấn đề này.
Lothar Kraner

Trong 22 năm làm việc với SQL Server, tôi KHÔNG BAO GIỜ thấy một trường hợp mà những gì được cho là xảy ra bởi Lothar đã xảy ra. Tôi đã làm việc tại một dự án sử dụng GUID làm chỉ mục cụm và GUID khiến cơ sở dữ liệu bị sập. Tám năm sau, hệ thống máy tính chậm cho phép những kẻ giết người ra sớm dallasnews.com/news/crime/2016/08/25/ .
Duane Lawrence

6

Tôi chưa bao giờ thấy một cột danh tính không phải là một chỉ mục, thường là Khóa chính.

Bây giờ chúng ta cần phân biệt Khóa chính (PK) và Chỉ mục cụm (CI), đầu tiên là tất cả về logic của lược đồ cơ sở dữ liệu, Khóa chính là thứ tạo ra một hàng khác với tất cả các hàng khác trong bảng và Khóa ngoài cho các bảng khác. Cột nhận dạng luôn là Khóa Ứng viên, nhưng đó là giả tạo và bạn có thể muốn Khóa Ứng viên tự nhiên là PK.

Thay vào đó, Indexed Index là về cách chỉ mục sẽ được tạo từ dữ liệu và được lưu trữ. Chỉ có thể có một chỉ mục được nhóm và nó sẽ là chỉ mục duy nhất đề cập đến dữ liệu trong bảng. Tất cả các chỉ mục khác sẽ đề cập đến một cụm.

Thông thường PK cũng là CI, nhưng đó đơn giản là hành vi mặc định. Tôi đã thấy, và đôi khi được tạo ra, PK không phải CI: PK là Khóa tự nhiên, CI là cột nhận dạng. Điều đó bởi vì, đơn giản hóa cách thức hoạt động của chỉ mục, dữ liệu trong định nghĩa CI càng nhỏ, chỉ số càng nhanh và CI cần phải càng nhanh càng tốt, vì vậy trong trường hợp PK rất lớn có cột nhận dạng như lập chỉ mục cụm và làm cho PK không phân cụm sẽ cải thiện hiệu suất.

Vì vậy, theo tôi, sử dụng một cột danh tính làm chỉ mục được nhóm không phải là một ý tưởng tồi, nhưng điều đó không có nghĩa là nó cũng phải là khóa chính.

Kịch bản duy nhất tôi có thể nghĩ về nơi một cột danh tính có thể là một lựa chọn tồi là khi có một khối lượng dữ liệu đến quá lớn đến nỗi ngay cả việc tạo ra danh tính cũng sẽ đạt hiệu suất.


5

Những khóa / chỉ mục nào để phân cụm không phải là một môn khoa học chính xác - việc sử dụng tốt nhất một chỉ mục được phân cụm có thể khác nhau tùy thuộc vào cách sử dụng của bảng (và việc sử dụng các cột trong khóa đó).

Khóa cụm hiệu quả hơn cho các truy vấn chọn ra nhiều hàng trong một phạm vi do không cần tìm kiếm thêm hàng để tìm dữ liệu cho các hàng được tìm thấy sau khi tìm kiếm chỉ mục. Nó cũng giúp cho việc tra cứu hàng đơn, nhưng sự khác biệt không đáng chú ý. Chẳng hạn, chúng ta có một bảng thường được tìm kiếm bởi ID chủ sở hữu đối tượng (chứ không phải ID đối tượng là khóa chính), do đó, ứng dụng của chúng ta sẽ có chỉ mục trên cột đó là khóa cụm, hiệu quả hơn là đôi khi tốt hơn nhiều để có khóa cụm trên các cột ngày được tham chiếu phổ biến nếu các hàng trên phạm vi ngày thường được tìm kiếm.

Nếu PK của một bảng nhất định thường là mục tiêu tham gia thì việc phân cụm PK của nó có thể giúp ích cho các hoạt động tham gia nhất định, việc giảm các lần tra cứu trang tiếp theo có thể là một phần thưởng lớn và tất nhiên nếu bạn có PK dựa trên dữ liệu thực (chứ không phải là một khóa thay thế như số tăng tự động hoặc UUID) có thể truy vấn theo phạm vi, nó có những lợi ích bạn mong đợi. Những lý do này là lý do khiến PK của bạn được phân cụm nói chung là một vị trí tốt để bắt đầu trước khi các cân nhắc khác được tính đến, và do đó đây là một khuyến nghị phổ biến (và đôi khi là mặc định được áp dụng tự động).

Như một lưu ý phụ: nếu bạn kết thúc bằng cách sử dụng cột UUID thay vì loại số nguyên tăng dần như PK trên bảng thì việc phân cụm trên đó có thể gây hại cho hiệu suất vì trang phụ tách ra được tạo bằng cách chèn dữ liệu "ngẫu nhiên" vào chỉ mục ( mỗi trang phân chia trên chỉ mục được phân cụm dẫn đến hoạt động IO bổ sung trên tất cả các chỉ mục khác trên bảng) làm chậm quá trình chèn và có thể làm trầm trọng thêm các vấn đề phân mảnh theo thời gian. Vì vậy, trong tình huống này thường có thể tốt hơn nhiều khi phân cụm một chỉ mục khác nhau (hoặc đôi khi không có một chỉ mục cụm nào cả , mặc dù điều này là không thể đối với SQL Server cho Azure [1] và rất hiếm khi không có khóa phân cụm một lợi ích hơn là gây bất lợi cho tổng thể).

[1] hiện tại có thể có một đống (một bảng không có khóa phân cụm) trên Azure SQL trong một thời gian, mặc dù với những cảnh báo tương tự như được tìm thấy trong Máy chủ SQL trước đó hiếm khi là một ý tưởng tuyệt vời


3

Tôi muốn một số ví dụ, trong các tình huống thực tế, trong đó sử dụng một cột danh tính làm chỉ mục cụm là một ý tưởng tồi.

Nói chung, đó là một ý tưởng tồi bất cứ khi nào danh tính Clustered Index chỉ đơn giản là một chỉ mục dư thừa. Bạn chỉ nhận được một chỉ mục được nhóm, vì vậy nếu bạn chọn sai chỉ mục, nó sẽ thêm chi phí cho tất cả các giao dịch của bạn.

Bất cứ khi nào bạn đã cần một khóa ghép hoặc một khóa tự nhiên, có một cột định danh là một chỉ mục được nhóm là một ý tưởng tồi.

Hai kịch bản phổ biến nên sử dụng khóa ghép là "bảng liên kết" và "bảng lồng nhau", vd:

create table a(id int identity primary key)
create table b(id int identity primary key)
create table a_b
( 
  a_id int not null references a,
  b_id int not null references b,
  constraint pk_a_b primary key (a_id,b_id),
  constraint ak_a_b unique (b_id, a_id)
)

Thêm một chỉ mục cụm danh tính là vô dụng và có hại.

Một ví dụ phổ biến của bảng thứ hai là các bảng "lồng nhau", trong đó một PK ghép đơn là chỉ số duy nhất cần thiết:

create table a(id int identity primary key)
create table a_detail
(
  a_id int not null references a,
  id int not null identity, 
  constraint pk_a_detail primary key (a_id,id) 
)

Các trường hợp sử dụng không gây tranh cãi cho các khóa tự nhiên bao gồm các bảng tra cứu, ví dụ:

create table region
(
  region_code char(3) not null primary key,
  name nvarchar(200),
  description nvarchar(200)
)

Hơi gây tranh cãi hơn một chút, nhưng IMO chính xác, là việc sử dụng UNIQUEIDENTIFIER tuần tự như một cụm PK, và do đó, đó cũng là một kịch bản trong đó việc thêm một cột IDENTITY với một chỉ mục cụm là có hại.


0

Nếu bạn đang triển khai các bảng chi tiết và muốn giữ lại khóa chính một cột, đây là một cách tiếp cận đáng để xem xét:

CREATE TABLE Parent (
    Parent_ID int NOT NULL IDENTITY(1,1),
    Parent_Data varchar(100) NULL,
    CONSTRAINT PK_Parent PRIMARY KEY CLUSTERED (Parent_ID)
);

CREATE TABLE Parent_Detail (
    Parent_Detail_ID int NOT NULL IDENTITY(1,1),
    Parent_ID int NOT NULL,
    Detail_Data varchar(100) NULL,
    CONSTRAINT PK_Parent_Detail PRIMARY KEY NONCLUSTERED (Parent_Detail_ID),
    INDEX CX_Parent_Detail UNIQUE CLUSTERED (Parent_ID, Parent_Detail_ID),
    CONSTRAINT FK_Parent_Detail_Parent FOREIGN KEY (Parent_ID) REFERENCES Parent (Parent_ID)
);

Tôi thiết lập bảng Parent với khóa chính được nhóm trên cột định danh. Đối với bảng Parent_Detail, cột định danh là khóa chính, nhưng chỉ mục phân cụm nằm trên khóa ngoại (Parent_ID) theo sau là cột định danh. Bằng cách thêm cột định danh vào chỉ mục phân cụm (như David Browne đã làm trong giải pháp của mình) và sau đó xác định chỉ mục phân cụm là duy nhất, chúng tôi tránh được trình duy nhất 4 byte. Mặc dù trình duy nhất chỉ được thêm khi cần thiết cho các bản ghi cụ thể ( https://sqlquantumleap.com/2017/09/18/clustered-index-uniquifier-existence-and-size/ có cách viết tốt về điều này), tôi cảm thấy tốt hơn xác định các chỉ mục cụm không chính của tôi với UNIQUE khi có thể.

Bằng cách phân cụm với cột Parent_ID ở vị trí hàng đầu, chúng tôi cho phép quét phạm vi chỉ mục được phân cụm để xác định các bản ghi chi tiết cho một bản ghi cha cụ thể, giúp cải thiện hiệu suất cho trường hợp sử dụng phổ biến này.

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.