Các thực tiễn tốt nhất để sử dụng GUID làm khóa chính, cụ thể là về hiệu suất là gì?


336

Tôi có một ứng dụng sử dụng GUID làm Khóa chính trong hầu hết các bảng và tôi đã đọc được rằng có vấn đề về hiệu năng khi sử dụng GUID làm Khóa chính. Thành thật mà nói, tôi chưa thấy vấn đề gì, nhưng tôi sắp bắt đầu một ứng dụng mới và tôi vẫn muốn sử dụng GUID làm Khóa chính, nhưng tôi đã nghĩ đến việc sử dụng Khóa chính tổng hợp (GUID và có thể là một trường khác .)

Tôi đang sử dụng GUID vì chúng rất đẹp và dễ quản lý khi bạn có các môi trường khác nhau như cơ sở dữ liệu "sản xuất", "thử nghiệm" và "dev" và cả dữ liệu di chuyển giữa các cơ sở dữ liệu.

Tôi sẽ sử dụng Entity Framework 4.3 và tôi muốn gán Guid trong mã ứng dụng, trước khi chèn nó vào cơ sở dữ liệu. (tức là tôi không muốn để SQL tạo Hướng dẫn).

Cách thực hành tốt nhất để tạo Khóa chính dựa trên GUID là gì, để tránh các lần truy cập hiệu suất được cho là có liên quan đến phương pháp này?


20
Vấn đề không được cho là. Nếu PK của bạn được phân cụm thì hầu như mọi thao tác chèn đều có khả năng gây ra sự phân tách trang. Trong các phiên bản hiện đại của SQL Server, điều này đã được "sửa" với NEWSEQUENTIALID (), nhưng điều này làm mất lợi ích của việc có thể tính toán trước. Tôi thực sự khuyên bạn nên đọc các GUID ở nơi khác vì đây là một câu hỏi quá rộng và có khả năng sẽ thu hút một trận chiến tôn giáo sẽ diễn ra trong nhiều giờ ...
Aaron Bertrand

4
Tôi cũng nói thêm rằng máy chủ từ không rõ ràng trong Tôi muốn gán Guid ở phía máy chủ (không muốn để SQL tạo GUID) .
Erik Philips

Câu hỏi này có những điểm tương đồng với "thuật toán sql-server-guide-sort-why-why" stackoverflow.com/questions/7810602/ mẹo
Clinton Ward

Câu trả lời:


494

GUID dường như là một lựa chọn tự nhiên cho khóa chính của bạn - và nếu bạn thực sự cần, có lẽ bạn có thể tranh luận để sử dụng nó cho KHÓA CHÍNH của bảng. Điều tôi khuyên bạn không nên làm là sử dụng cột GUID làm khóa phân cụm , mà SQL Server thực hiện theo mặc định, trừ khi bạn đặc biệt không nói với nó.

Bạn thực sự cần phải tách biệt hai vấn đề:

  1. các khóa chính là một cấu trúc logic - một trong các phím ứng cử viên duy nhất và đáng tin cậy xác định mỗi hàng trong bảng. Đây có thể là bất cứ điều gì, thực sự - một INT, một GUID, một chuỗi - chọn những gì có ý nghĩa nhất cho kịch bản của bạn.

  2. các chìa khóa phân nhóm (cột hoặc cột mà xác định "nhóm chỉ số" trên bảng) - đây là một chất điều lưu trữ liên quan đến, và ở đây, một nhỏ, ổn định, ngày càng tăng kiểu dữ liệu là lựa chọn tốt nhất của bạn - INThoặc BIGINTlà bạn tùy chọn mặc định.

Theo mặc định, khóa chính trên bảng SQL Server cũng được sử dụng làm khóa phân cụm - nhưng điều đó không cần phải như vậy! Cá nhân tôi đã thấy hiệu suất tăng đáng kể khi chia khóa Chính / cụm được dựa trên GUID trước đó thành hai khóa riêng biệt - khóa chính (logic) trên GUID và khóa phân cụm (đặt hàng) trên một INT IDENTITY(1,1)cột riêng .

Như Kimberly Tripp - Nữ hoàng lập chỉ mục - và những người khác đã tuyên bố rất nhiều lần - GUIDvì khóa phân cụm không tối ưu, do tính ngẫu nhiên của nó, nó sẽ dẫn đến phân mảnh trang và chỉ mục lớn và hiệu suất nói chung rất tệ.

Vâng, tôi biết - có newsequentialid()trong SQL Server 2005 trở lên - nhưng ngay cả điều đó không thực sự và đầy đủ theo trình tự và do đó cũng bị các vấn đề tương tự như GUID- chỉ kém một chút nổi bật như vậy.

Sau đó, có một vấn đề khác cần xem xét: khóa phân cụm trên bảng sẽ được thêm vào từng mục nhập trên mỗi và mọi chỉ mục không phân cụm trên bảng của bạn - do đó bạn thực sự muốn đảm bảo rằng nó càng nhỏ càng tốt. Thông thường, một hơn INT2 tỷ hàng sẽ đủ cho phần lớn các bảng - và so với GUIDkhóa phân cụm, bạn có thể tiết kiệm cho mình hàng trăm megabyte dung lượng lưu trữ trên đĩa và trong bộ nhớ máy chủ.

Tính toán nhanh - sử dụng INTso với GUIDkhóa chính và cụm:

  • Bảng cơ sở với 1000'000 hàng (3,8 MB so với 15,26 MB)
  • 6 chỉ mục không bao gồm (22,89 MB so với 91,55 MB)

TỔNG: 25 MB so với 106 MB - và đó chỉ là trên một bảng duy nhất!

Một số thực phẩm khác cho suy nghĩ - công cụ tuyệt vời của Kimberly Tripp - đọc nó, đọc lại, tiêu hóa nó! Đó là phúc âm lập chỉ mục SQL Server, thực sự.

Tái bút: tất nhiên, nếu bạn đang giải quyết chỉ vài trăm hoặc vài nghìn hàng - hầu hết các đối số này sẽ không thực sự ảnh hưởng đến bạn. Tuy nhiên: nếu bạn nhận được hàng chục hoặc hàng trăm ngàn hàng hoặc bạn bắt đầu đếm hàng triệu - thì những điểm đó trở nên rất quan trọng và rất quan trọng để hiểu.

Cập nhật: nếu bạn muốn có PKGUIDcột của mình làm khóa chính (nhưng không phải là khóa phân cụm của bạn) và một cột khác MYINT( INT IDENTITY) làm khóa phân cụm của bạn - hãy sử dụng:

CREATE TABLE dbo.MyTable
(PKGUID UNIQUEIDENTIFIER NOT NULL,
 MyINT INT IDENTITY(1,1) NOT NULL,
 .... add more columns as needed ...... )

ALTER TABLE dbo.MyTable
ADD CONSTRAINT PK_MyTable
PRIMARY KEY NONCLUSTERED (PKGUID)

CREATE UNIQUE CLUSTERED INDEX CIX_MyTable ON dbo.MyTable(MyINT)

Về cơ bản: bạn chỉ cần nói rõ ràng ràngPRIMARY KEY buộc rằng nó NONCLUSTERED(nếu không nó được tạo như là chỉ mục được nhóm của bạn, theo mặc định) - và sau đó bạn tạo một chỉ mục thứ hai được xác định làCLUSTERED

Điều này sẽ hoạt động - và đó là một tùy chọn hợp lệ nếu bạn có một hệ thống hiện tại cần được "thiết kế lại" để thực hiện. Đối với một hệ thống mới, nếu bạn bắt đầu từ đầu và bạn không ở trong một kịch bản sao chép, thì tôi luôn chọn ID INT IDENTITY(1,1)khóa chính của mình - hiệu quả hơn bất kỳ thứ gì khác!


2
Đây là một câu trả lời tuyệt vời, một điều tôi muốn đề cập là việc có thể tạo khóa trước khi chèn thường hữu ích. Việc sử dụng "new resultentialid ()" có thể giúp phân cụm, nhưng điều đó đòi hỏi một chuyến đi khứ hồi bổ sung cho SQL. Vì vậy, một lợi ích khác của phương pháp "khóa thay thế" là bạn có thể tạo id mới, phía máy khách, với ít mối quan tâm phân mảnh chỉ mục hơn.
Andrew Theken

2
Cách tôi đọc điều này là có cả cột định danh duy nhất không phân cụm và cột nhận dạng int, FK cũng nên là định danh duy nhất? Nếu bạn làm điều đó, khi nào bạn thực sự sẽ sử dụng cột danh tính trực tiếp, hoặc bạn sẽ không?
Pinkfloydx33

2
Câu hỏi nhỏ, bây giờ nên sử dụng GUID trên các phép nối hay id int? Bản năng của tôi cho tôi biết nên sử dụng GUID, nhưng tôi không thấy vấn đề kỹ thuật khi sử dụng int id ...
Nicolas Belley

3
@marc_s nhưng trong một kịch bản sao chép, nếu cột int là danh tính, chúng ta có nên sử dụng GUID không vì cột int có thể lặp lại trên các thiết bị?
Nicolas Belley

6
@Kipei: vấn đề chính là NẾU bạn có giá trị tự nhiên như vậy - thì có, bạn có thể sử dụng nó làm khóa chính. NHƯNG : các giá trị như DATETIMEví dụ KHÔNG hữu ích cho khóa phân cụm, vì chúng chỉ có độ chính xác 3,33ms và do đó có thể tồn tại các bản sao. Vì vậy, trong trường hợp như vậy, bạn * vẫn cần một INT IDENTITYthay thế - do đó, tôi thường sử dụng nó theo mặc định, vì hơn 20 năm kinh nghiệm của tôi, một khóa tự nhiên thực sự có thể sử dụng hầu như không tồn tại ....
marc_s

51

Tôi đã sử dụng GUID làm PK từ năm 2005. Trong thế giới cơ sở dữ liệu phân tán này, đó hoàn toàn là cách tốt nhất để hợp nhất dữ liệu phân tán. Bạn có thể kích hoạt và quên các bảng hợp nhất mà không phải lo lắng về việc kết hợp ints giữa các bảng đã tham gia. GUIDs tham gia có thể được sao chép mà không phải lo lắng.

Đây là thiết lập của tôi để sử dụng GUID:

  1. PK = HƯỚNG DẪN. GUID được lập chỉ mục tương tự như chuỗi, vì vậy các bảng hàng cao (hơn 50 triệu bản ghi) có thể cần phân vùng bảng hoặc các kỹ thuật hiệu suất khác. SQL Server đang trở nên cực kỳ hiệu quả, do đó mối quan tâm về hiệu suất ngày càng ít được áp dụng.

  2. PK Guid là chỉ số NON-Clustered. Không bao giờ lập chỉ mục một GUID trừ khi đó là NewSequentialID. Nhưng ngay cả sau đó, khởi động lại máy chủ sẽ gây ra sự phá vỡ lớn trong việc đặt hàng.

  3. Thêm ClusterID Int vào mỗi bảng. Đây là Chỉ số CLUSTERED của bạn ... yêu cầu bảng của bạn.

  4. Tham gia vào ClusterID (int) hiệu quả hơn, nhưng tôi làm việc với 20-30 triệu bảng ghi, vì vậy việc tham gia trên GUID không ảnh hưởng rõ rệt đến hiệu suất. Nếu bạn muốn hiệu suất tối đa, hãy sử dụng khái niệm ClusterID làm khóa chính của bạn và tham gia vào ClusterID.

Đây là bảng Email của tôi ...

CREATE TABLE [Core].[Email] (
    [EmailID]      UNIQUEIDENTIFIER CONSTRAINT [DF_Email_EmailID] DEFAULT (newsequentialid()) NOT NULL,        
    [EmailAddress] NVARCHAR (50)    CONSTRAINT [DF_Email_EmailAddress] DEFAULT ('') NOT NULL,        
    [CreatedDate]  DATETIME         CONSTRAINT [DF_Email_CreatedDate] DEFAULT (getutcdate()) NOT NULL,      
    [ClusterID] INT NOT NULL IDENTITY,
    CONSTRAINT [PK_Email] PRIMARY KEY NonCLUSTERED ([EmailID] ASC)
);
GO

CREATE UNIQUE CLUSTERED INDEX [IX_Email_ClusterID] ON [Core].[Email] ([ClusterID])
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_Email_EmailAddress] ON [Core].[Email] ([EmailAddress] Asc)

Bạn có thể giải thích ràng buộc PK_Email không? Tại sao bạn có ... NonClustered (EmailID ASC) thay vì ... Nonclustered (ClusterID ASC)?
Phil

2
Bạn đặt cược. Hai điều chính đang diễn ra với các chỉ mục: 1. Clustered trên ClusterID - Đặt hàng bảng của bạn trên đĩa (phân mảnh 0%). 2. NonClustered trên EmailID - Lập chỉ mục trường EmailID để tăng tốc độ tra cứu ID GUID. Tra cứu trường GUID xử lý chuỗi-ish, vì vậy việc tra cứu EmailID sẽ chậm nếu không có chỉ mục.
Robert J. Tốt

@ RobertJ. Tốt Tôi đã thấy phương pháp này được thảo luận trước khi tức là thêm khóa int thay thế vào cụm. Nhưng tôi không thể tìm thấy bất cứ nơi nào cho thấy hiệu suất đạt được khi có một chỉ số được nhóm thay thế bằng cách sử dụng một đống. Bạn có bất kỳ liên kết đến dữ liệu điểm chuẩn?
Dale K

1
Xin chào @DaleBurrell, chỉ mục được nhóm là để ngăn phân mảnh bảng. Hiệu suất đạt được xảy ra khi bảng tự nhiên phát triển theo thứ tự trên đĩa, với độ phân mảnh thấp.
Robert J. Tốt

@ RobertJ. Tốt Đó có phải là một ứng dụng web? Bạn đang sử dụng gì trong url / hrefs? hướng dẫn hay int?
dariol

10

Tôi hiện đang phát triển một ứng dụng web với EF Core và đây là mẫu tôi sử dụng:

Tất cả các lớp của tôi (bảng) và một PK và FK int. Tôi đã có một cột bổ sung với loại Hướng dẫn (được tạo bởi hàm tạo c #) với chỉ mục không được nhóm trên đó.

Tất cả các phép nối của bảng trong EF được quản lý thông qua các phím int trong khi tất cả các truy cập từ bên ngoài (bộ điều khiển) được thực hiện với Hướng dẫn.

Giải pháp này cho phép không hiển thị các phím int trên url nhưng giữ cho mô hình gọn gàng và nhanh chóng.


Có bất cứ điều gì bạn cần làm để định cấu hình số nguyên pK thành cụm, như chú thích dữ liệu, hoặc nó chỉ được cấu hình tự động?
Allen Wang

Tên của tài sản bạn sử dụng cho Guid là gì?
Trọng Phan

3

Nếu bạn sử dụng GUID làm khóa chính và tạo chỉ mục được nhóm thì tôi khuyên bạn nên sử dụng giá trị NEWSEQUENTIALID () mặc định cho nó


Tại sao bạn lại làm vậy?
genuinefafa

3

Liên kết này nói rằng nó tốt hơn tôi có thể và giúp trong việc ra quyết định của tôi. Tôi thường chọn một int là khóa chính, trừ khi tôi không có nhu cầu cụ thể và tôi cũng để máy chủ SQL tự động tạo / duy trì trường này trừ khi tôi không có lý do cụ thể nào đó. Trong thực tế, mối quan tâm về hiệu suất cần được xác định dựa trên ứng dụng cụ thể của bạn. Có nhiều yếu tố đang chơi ở đây bao gồm nhưng không giới hạn ở kích thước db dự kiến, lập chỉ mục phù hợp, truy vấn hiệu quả và hơn thế nữa. Mặc dù mọi người có thể không đồng ý, tôi nghĩ trong nhiều tình huống bạn sẽ không nhận thấy sự khác biệt với một trong hai tùy chọn và bạn nên chọn cái nào phù hợp hơn cho ứng dụng của mình và điều gì cho phép bạn phát triển dễ dàng hơn, nhanh hơn và hiệu quả hơn (Nếu bạn không bao giờ hoàn thành ứng dụng những gì khác biệt làm cho phần còn lại :).

https://web.archive.org/web/20120812080710/http://database.aspfaq.com/database/what-should-i-choose-for-my-primary-key.html

Tái bút: Tôi không chắc tại sao bạn lại sử dụng PK tổng hợp hoặc lợi ích mà bạn tin rằng sẽ mang lại cho bạn.


Hoàn toàn đồng ý!! Nhưng điều đó có nghĩa là nếu tôi có GUID là PK hoặc PK tổng hợp với GUID và trường khác sẽ giống nhau phải không?
VAAA

1
PK (chỉ mục) sẽ được tạo thành từ hai cột, nhưng trừ khi bạn có một số lý do cụ thể về kinh doanh để làm điều này, có vẻ như không cần thiết.
Matt

1
BTW câu hỏi này là một trong những câu hỏi phân cực và gây tranh cãi nhất hiện có và do đó cực kỳ khó để có câu trả lời mà bạn sẽ cảm thấy thoải mái 100%. Cả hai phương pháp đều đi kèm với sự đánh đổi, thật may mắn :)
Matt


0

Có ID tuần tự giúp hacker dễ dàng hơn rất nhiều để khai thác dữ liệu và dữ liệu của bạn. Hãy ghi nhớ điều đó khi chọn PK cho trang web.


Bạn có thể cung cấp bất kỳ logic hoặc bằng chứng để sao lưu yêu cầu này? Tôi đang đấu tranh để xem làm thế nào một id tuần tự có thể ảnh hưởng đến bảo mật.
jonaglon

Chắc chắn, nếu bạn biết số ID là số nguyên, bạn có thể đoán các bản ghi tuần tự trong một DB. Vì vậy, nếu bạn truy vấn một mục duy nhất, bạn có thể nói rằng mục tiếp theo là pk + 1. Nếu bạn có GUIDS ngẫu nhiên, nó sẽ không theo một mẫu. Gần như không thể truy vấn các bản ghi khác ngoài bản ghi bạn đã truy vấn trước đó (Và biết PK).
DaBlue

1
Nếu tin tặc có thể truy vấn cơ sở dữ liệu của bạn mà bạn đã bị xâm nhập, tôi không thể thấy id tuần tự làm cho tình hình tồi tệ hơn như thế nào.
jonaglon

1
Nếu người dùng có thể chuyển 1012 cho một số khác và xem dữ liệu họ không nên thì đó là một vấn đề bảo mật rất nghiêm trọng, vấn đề đó không phải do lựa chọn khóa chính mà nó bị làm trầm trọng thêm. Tôi có quan điểm của bạn, cảm ơn bạn đã đánh vần nó.
jonaglon

2
Bạn có thể sử dụng GUID để xác định bản ghi tại trang web, đó không phải là PK của bảng. Sử dụng tham số truy vấn trong một trang web sẽ không xác định cách bạn cấu trúc lược đồ DB của bạn. PK không liên quan gì đến đầu vào và tham số trong giao diện người dùng hoặc hệ thống phụ trợ.
Panos Roditakis
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.