Sử dụng GUID làm khóa chính


32

Tôi thường sử dụng ID gia tăng tự động làm Khóa chính trong cơ sở dữ liệu. Tôi đang cố gắng tìm hiểu những lợi ích của việc sử dụng GUID. Tôi đã đọc bài viết này: https://betterexplained.com/articles/the-quick-guide-to-guids/

Tôi nhận ra rằng các GUID này được sử dụng để xác định các đối tượng ở cấp ứng dụng. Có phải chúng cũng được lưu trữ như là khóa chính ở cấp cơ sở dữ liệu. Ví dụ, giả sử tôi có lớp sau:

public class Person
{
public GUID ID;
public string Name;
..

//Person Methods follow
}

Nói rằng tôi muốn tạo một người mới trong bộ nhớ và sau đó chèn Người vào cơ sở dữ liệu. Tôi chỉ có thể làm điều này:

Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

Giả sử tôi có cơ sở dữ liệu chứa hàng triệu và hàng triệu hàng với GUID làm Khóa chính. Điều này sẽ luôn luôn là duy nhất? Tôi thậm chí có hiểu chính xác GUID không?

Tôi đã đọc bài viết này sớm hơn: http : //enterpriseccraft Skill.com/2014/11/15/cqs-with-database-generated-ids/ . Nó làm tôi bối rối một chút vì nó dường như đề xuất một phương tiện hạnh phúc giữa GUID và số nguyên làm Khóa chính.

Chỉnh sửa 11/06/18

Tôi đã tin rằng Guids phù hợp hơn ints cho yêu cầu của tôi. Tôi đang sử dụng CQRS nhiều hơn những ngày này và GUID phù hợp hơn.

Tôi nhận thấy rằng một số nhà phát triển mô hình hóa các GUID dưới dạng chuỗi trong mô hình miền, ví dụ như ở đây: https://github.com/dotnet-arch architecture / eShopOnContainers / blob / dev / src / Service / ORdering / Domain.DAggregatesModel / Banger Người mua.cs - trong trường hợp này: IdentityGuid là một GUID được mô hình hóa dưới dạng chuỗi. Có bất kỳ lý do để làm điều này ngoài những gì được nêu ở đây: Sử dụng một đối tượng giá trị tùy chỉnh hoặc Hướng dẫn làm định danh thực thể trong một hệ thống phân tán? . Việc mô hình hóa GUID thành một chuỗi là "bình thường" hay tôi nên mô hình hóa nó như một GUID trong mô hình và cơ sở dữ liệu?



7
Không được đảm bảo là duy nhất, mặc dù không chắc bạn sẽ thấy va chạm. stackoverflow.com/questions/1155008/how-unique-is-uuid/ từ
icirellik

2
xem thêm: Va chạm UUID
gnat

2
Xem thêm dba.stackexchange.com/questions/54690/ , cũng như rất nhiều câu hỏi khác - chủ đề này đã được hỏi, và trả lời, và thường xuyên tranh luận.
Greenstone Walker

1
Hệ thống tôi đang làm việc với hiện tại sử dụng UUID. Một thuộc tính tốt là ID xác định duy nhất một bản ghi, trái ngược với ID tuần tự xác định một bản ghi trong bảng đó.
Justin

Câu trả lời:


41

GUID theo định nghĩa "IDentifier duy nhất toàn cầu". Có một khái niệm tương tự nhưng hơi khác trong Java được gọi là UUID "IDentifier duy nhất toàn cầu". Các tên có thể hoán đổi cho nhau để sử dụng thực tế.

GUID là trọng tâm trong cách Microsoft hình dung phân cụm cơ sở dữ liệu để hoạt động và nếu bạn cần kết hợp dữ liệu từ các nguồn được kết nối đôi khi, chúng thực sự giúp ngăn chặn xung đột dữ liệu.

Một số sự kiện Pro-GUID:

  • GUID ngăn va chạm quan trọng
  • GUID giúp hợp nhất dữ liệu giữa các mạng, máy, v.v.
  • SQL Server có hỗ trợ cho GUIDS bán tuần tự để giúp giảm thiểu phân mảnh chỉ mục ( ref , một số cảnh báo)

Một số Ugliness với GUID

  • Chúng lớn, mỗi 16 byte
  • Chúng không theo thứ tự, vì vậy bạn không thể sắp xếp ID và hy vọng nhận được thứ tự chèn như bạn có thể trên id tăng tự động
  • Chúng cồng kềnh hơn khi làm việc, đặc biệt là trên các tập dữ liệu nhỏ (như tra bảng)
  • Việc triển khai GUID mới mạnh mẽ hơn trên SQL Server so với trong thư viện C # (bạn có thể có GUIDS tuần tự từ SQL Server, trong C # là ngẫu nhiên)

GUID sẽ làm cho các chỉ mục của bạn lớn hơn, do đó chi phí không gian đĩa của việc lập chỉ mục một cột sẽ cao hơn. GUID ngẫu nhiên sẽ phân đoạn các chỉ mục của bạn.

Nếu bạn biết bạn sẽ không đồng bộ hóa dữ liệu từ các mạng khác nhau, GUID có thể mang nhiều chi phí hơn giá trị.

Nếu bạn có nhu cầu nhập dữ liệu từ các máy khách đôi khi được kết nối, chúng có thể mạnh mẽ hơn rất nhiều để ngăn chặn các xung đột chính so với việc dựa vào cài đặt phạm vi trình tự cho các máy khách đó.


18
Hiểu biết của tôi là GUID đồng nghĩa với UUID. UUID là tên tiêu chuẩn. GUID là những gì Microsoft đặt ra cho họ trước RFC 4122 .
JimmyJames

13
"Chúng không có thứ tự, vì vậy bạn không thể sắp xếp ID và hy vọng có được thứ tự chèn như bạn có thể trên id tăng tự động" Thành thật mà nói, tôi cũng không thoải mái khi dựa vào đó với id thông thường. Mặc dù có thể trong trường hợp cực hạn để id thấp hơn được cam kết vào đĩa sau, tôi thà dựa vào dữ liệu sắp xếp hữu ích, như dấu thời gian chèn. Id nên được coi như địa chỉ bộ nhớ - mọi thứ đều có một, nhưng bản thân giá trị là vô nghĩa. Sử dụng chúng cho tiebreakers nhiều nhất. Đặc biệt là nếu bạn đã tải một lượng lớn, thứ tự chèn không được đảm bảo.
Clockwork-Muse

8
@CortAmmon Theo WikipediaRFC 4122 , chúng đồng nghĩa với nhau. P. Leach của Microsoft là một trong những người tạo ra RFC. Tôi nghĩ kể từ khi RFC được tạo ra, hai cái này giống nhau. Từ RFC: "UUID (IDentifier duy nhất toàn cầu), còn được gọi là GUID (IDentifier duy nhất toàn cầu)." Tôi nghĩ cũng hữu ích khi lưu ý rằng GUID không được tạo bởi MS. Họ chỉ tạo ra một tên mới cho một công nghệ được thông qua từ nơi khác.
JimmyJames

6
"SQL Server có tối ưu hóa để xử lý GUID vì vậy nó không ảnh hưởng nhiều đến hiệu năng truy vấn." -1 Gần như không được tối ưu hóa đủ. Tôi đang làm việc với DB trong đó tất cả các PK đều là hướng dẫn và đó là một trong những nguyên nhân chính dẫn đến hiệu suất kém.
Andy

7
"SQL Server có tối ưu hóa để xử lý GUID vì vậy nó không ảnh hưởng nhiều đến hiệu năng truy vấn. " Không đúng. Tuyên bố đó giả định các loại dữ liệu khác không được tối ưu hóa. Máy chủ cơ sở dữ liệu cũng có tối ưu hóa để xử lý các giá trị int đơn giản, ví dụ. GUID / UUID chậm hơn rất nhiều so với sử dụng giá trị int 4 byte. 16 byte sẽ không bao giờ nhanh bằng 4 byte - đặc biệt là trên một máy xử lý tối đa 4 hoặc 8 byte nguyên bản.
Andrew Henle

28

Điều này sẽ luôn luôn là duy nhất?

Luôn luôn? không, không phải lúc nào cũng vậy; đó là một chuỗi hữu hạn của các bit.

Giả sử tôi có cơ sở dữ liệu chứa hàng triệu và hàng triệu hàng với GUID làm Khóa chính.

Hàng triệu và hàng triệu, bạn có thể an toàn. Một triệu triệu, và khả năng va chạm trở nên đáng kể. Tuy nhiên, có một tin tốt: bạn đã hết dung lượng đĩa vào thời điểm đó.

Tôi có thể làm điều này?

Bạn có thể; nó không phải là một ý tưởng hoàn toàn tốt Mô hình miền của bạn thường không nên tạo số ngẫu nhiên; chúng nên là đầu vào cho mô hình của bạn.

Ngoài ra, khi bạn đang xử lý một mạng không đáng tin cậy, nơi bạn có thể nhận được các tin nhắn trùng lặp, một UUID được tạo một cách xác định sẽ bảo vệ bạn khỏi có các thực thể trùng lặp. Nhưng nếu bạn chỉ định một số ngẫu nhiên mới cho mỗi số, thì bạn có nhiều việc phải làm để xác định sự trùng lặp.

Xem mô tả về uuid dựa trên tên trong RFC 4122

Việc mô hình hóa GUID thành một chuỗi là "bình thường" hay tôi nên mô hình hóa nó như một GUID trong mô hình và cơ sở dữ liệu?

Tôi không nghĩ nó quan trọng lắm. Đối với hầu hết các mô hình miền của bạn, nó là một định danh ; truy vấn duy nhất bạn yêu cầu là liệu nó có giống với một số định danh khác hay không. Mô hình miền của bạn thường sẽ không nhìn vào biểu diễn trong bộ nhớ của một mã định danh.

Nếu GUID có sẵn dưới dạng "loại nguyên thủy" trong cài đặt không biết tên miền của bạn, tôi sẽ sử dụng nó; nó cho phép bối cảnh hỗ trợ để chọn tối ưu hóa phù hợp có thể có sẵn.

Tuy nhiên, điều bạn nên nhận ra là sự thể hiện của mã định danh, cả trong bộ nhớ và trong bộ lưu trữ, là một quyết định mà bạn đang đưa ra khi thực hiện và do đó bạn nên thực hiện các bước để đảm bảo rằng dấu chân của mã được ghép với điều đó quyết định là nhỏ - xem Parnas 1972 .


20
+1 cho "bạn đã hết dung lượng đĩa vào thời điểm đó xảy ra."
w0051977

2
Tôi cảm thấy khái niệm " deterministically tạo UUID " là điều cần thiết (xem Data Vault 2)
alk

Thật vậy, việc có thể tính lại UUID / GUID dựa trên dữ liệu khác là một sự trợ giúp to lớn, đặc biệt là để phát hiện các bản sao. Tôi đã từng xây dựng một hệ thống xử lý tin nhắn lưu trữ các tin nhắn và đẩy chúng qua một đường ống xử lý. Tôi đã tạo một hàm băm của tin nhắn và sử dụng nó làm khóa chính trong toàn hệ thống. chỉ có điều, tự nó đã giải quyết cho tôi rất nhiều vấn đề để xác định thông điệp khi chúng tôi phải mở rộng quy mô.
Newtopian

Một triệu triệu = 2 ^ 40. Điều đó làm cho 2 ^ 79 cặp va chạm có thể xảy ra. GUID có 2 ^ 128 bit, vì vậy cơ hội là một trong 2 ^ 49. Nhiều khả năng là bạn có một lỗi sử dụng lại cùng một GUID cho hai bản ghi hoặc nhầm tưởng rằng có một sự va chạm không có.
gnasher729

Tôi sẽ trở lại thông qua các câu hỏi lịch sử của tôi. Trước khi tôi chấp nhận; bạn có thể xem chỉnh sửa của tôi?
w0051977

11

HƯỚNG DẪN hoặc UUID rất có thể là duy nhất vì cách chúng được tạo và chúng cung cấp một cách an toàn để đảm bảo tính duy nhất mà không phải liên lạc với cơ quan trung ương.

Lợi ích của GUID là khóa chính:

  • Bạn có thể sao chép dữ liệu giữa các phân đoạn khác nhau của cụm và không cần phải lo lắng về các va chạm PK.
  • Nó cho phép bạn biết khóa chính của mình trước khi bạn chèn bất kỳ bản ghi nào.
  • Đơn giản hóa logic giao dịch để chèn hồ sơ con.
  • Không thể dễ dàng đoán ra.

Trong ví dụ bạn cung cấp:

Person p1 = new Person();
p1.ID = GUID.NewGUID();
PersonRepository.Insert(p1);

Chỉ định GUID trước thời gian chèn có thể lưu một chuyến đi khứ hồi vào cơ sở dữ liệu khi chèn các bản ghi con liên tiếp và cho phép bạn cam kết chúng trong cùng một giao dịch.

Person p2 = new Person();
p2.ParentID = p1.ID
PersonRepository.Insert(p2);

Những bất lợi cho GUID là khóa chính:

  • Chúng có dung lượng lớn 16 byte nghĩa là chúng sẽ tiêu tốn nhiều dung lượng hơn khi các chỉ mục và khóa ngoại được thêm vào.
  • Họ không sắp xếp tốt vì về cơ bản chúng là những con số ngẫu nhiên.
  • Chỉ số sử dụng là rất, rất, rất xấu.
  • Rất nhiều lá di chuyển.
  • Họ rất khó nhớ.
  • Họ rất khó để diễn đạt bằng lời.
  • Họ có thể làm cho URL khó đọc hơn.

Nếu ứng dụng của bạn không có nhu cầu sắp xếp hoặc phân cụm, tốt nhất bạn nên sử dụng loại dữ liệu nhỏ hơn, đơn giản hơn như int hoặc bigint.

Nhiều cơ sở dữ liệu có triển khai nội bộ của mình mà cố gắng để giảm thiểu những vấn đề lưu trữ do của GUID và SQL Server thậm chí còn có một chức năng newsequentialid để giúp đỡ với việc đặt hàng của UUID của phép sử dụng tốt hơn các chỉ số và họ thường có những đặc điểm hiệu suất tốt hơn.

Ngoài ra, từ góc độ của người kiểm tra, người dùng hoặc nhà phát triển làm việc với ứng dụng, sử dụng ID qua GUID sẽ cải thiện đáng kể giao tiếp. Hãy tưởng tượng bạn phải đọc một GUID qua điện thoại.

Cuối cùng, trừ khi các cụm phân cụm hoặc làm mờ các URL quy mô lớn là một yêu cầu thực tế hơn để gắn với các ID tăng tự động.


1
Một điều cần xem xét là tùy thuộc vào loại UUID , chúng chứa thông tin có khả năng có thể được sử dụng để xác định máy mà chúng được tạo. Các biến thể ngẫu nhiên thuần túy có thể có nhiều khả năng va chạm mà không có đủ entropy. Điều này nên được xem xét trước khi sử dụng trong một URI.
JimmyJames

Đồng ý, mặc dù người ta không bao giờ nên để lộ khóa chính của họ trong một URL. Một số phương pháp thích hợp hơn nên được sử dụng để đảm bảo rằng không có rò rỉ dữ liệu an toàn cho hệ thống bên
ngoài.s

1
Có thêm một trường hợp sử dụng: cơ sở dữ liệu OLTP chèn nặng trong đó khóa cho chuỗi là một nút cổ chai. Theo người bạn Oracle DBA của tôi, điều này không hiếm như âm thanh, bạn thậm chí không cần quy mô lớn hoặc cụm cho điều đó. • Cuối cùng, hãy cân nhắc ưu và nhược điểm (và đừng nhầm lẫn giữa ưu và nhược điểm của UUID với ưu / nhược điểm không dành riêng cho UUID như một số áp phích đã làm) và đo lường .
mirabilos

1
Nếu bạn sử dụng new resultentialid thì bạn phải truy cập db để lấy id (như với một định danh int), phải không? Lợi ích ở đây là gì
w0051977

1
@mirabilos Để rõ ràng, khi tôi nói khủng khiếp, cuối cùng chúng tôi đã có những phần chèn mất vài phút mỗi hàng. Nó bắt đầu ổn nhưng sau khi có 10 ngàn hàng, nó đi rất nhanh. Nếu nó không rõ ràng, 10 hàng ngàn hàng là một bảng rất nhỏ.
JimmyJames

4

Tôi muốn nói không, đừng sử dụng GUID làm khóa chính. Tôi thực sự đang đối phó với một DB như vậy và chúng là một trong những nguyên nhân chính gây ra các vấn đề về hiệu năng.

Thêm 12 byte cộng lại nhanh chóng; hãy nhớ rằng, hầu hết các PK sẽ là FK trong các bảng khác và chỉ có ba FK trong một bảng mà bạn hiện có thêm 48 byte cho mỗi hàng. Điều đó cộng lại trong bảng và trong các chỉ mục. Nó cũng thêm vào trong I / O đĩa. 12 byte thêm đó cần phải được đọc và viết.

Và nếu bạn không sử dụng các hướng dẫn tuần tự và các PK được phân cụm (đó là những gì xảy ra theo mặc định), đôi khi SQL sẽ phải di chuyển toàn bộ các trang dữ liệu xung quanh để vắt nhiều hơn vào đúng "điểm". Đối với một cơ sở dữ liệu giao dịch cao với nhiều phần chèn, cập nhật và xóa, mọi thứ sẽ chậm lại.

Nếu bạn cần một số loại định danh duy nhất để đồng bộ hóa hoặc một cái gì đó, hãy thêm một cột hướng dẫn. Đừng biến nó thành PK.


4
Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

Đây là lý do quan trọng nhất để sử dụng GUID.

Việc bạn có thể tạo một id duy nhất mà không cần mã của bạn biết hoặc liên lạc với lớp kiên trì của bạn là một lợi ích rất lớn.

Bạn có thể chắc chắn rằng đối tượng Person bạn vừa tạo trên máy chủ, điện thoại pc, máy tính xách tay, thiết bị ngoại tuyến hoặc bất cứ thứ gì là duy nhất trên tất cả các máy chủ của bạn trên toàn thế giới được phân phối.

Bạn có thể dán nó vào bất kỳ loại cơ sở dữ liệu rdb hoặc no-sql, tệp nào, gửi nó đến bất kỳ dịch vụ web nào hoặc vứt nó đi ngay lập tức khi không cần thiết

Không, bạn sẽ không bao giờ có được một vụ va chạm.

Có chèn có thể chậm hơn một chút vì chỉ số có thể cần phải được xử lý.

Có nó lớn hơn một int.

  • chỉnh sửa. đã phải bắn ra trước khi kết thúc.

Tôi biết nhiều người cảm thấy mạnh mẽ về auto inc ints và đây là một chủ đề gây tranh cãi với các DBA

Nhưng tôi thực sự không thể tuyên bố đủ mạnh mẽ về những hướng dẫn vượt trội. Bạn nên sử dụng các hướng dẫn theo mặc định trong bất kỳ ứng dụng nào.

auto inc ints có nhiều sai sót

  • Bạn sử dụng db phân phối No-Sql. Bạn chỉ đơn giản là không thể nói chuyện với tất cả các trường hợp khác để tìm ra số tiếp theo là gì.

  • Bạn sử dụng một hệ thống xếp hàng tin nhắn. Mọi thứ cần ID trước khi chúng vào db

  • Bạn đang tạo một số mục và chỉnh sửa chúng trước khi lưu. Mỗi người cần một id trước khi bạn nhấn db

  • Bạn muốn xóa và chèn lại hàng. Hãy chắc chắn rằng bạn không đếm id id tự động của bạn và chạy ra ngoài!

  • Bạn muốn không tiết lộ số lượng Đơn đặt hàng bạn đã thực hiện trong năm nay cho mọi người dùng

  • Bạn muốn chuyển dữ liệu ẩn danh từ sản xuất sang kiểm tra và giữ nguyên các mối quan hệ. Nhưng không xóa tất cả các dữ liệu thử nghiệm hiện có.

  • Bạn muốn hợp nhất sản phẩm của người thuê nhà của bạn vào cơ sở dữ liệu nhiều người thuê nhưng mọi người đều có một đơn hàng 56.

  • Bạn tạo các đối tượng vẫn tồn tại nhưng phù du. (đơn đặt hàng chưa hoàn thành) một lần nữa, không sử dụng hết số int của bạn với những thứ không còn tồn tại.

Danh sách này là vô tận và tất cả chúng đều là những vấn đề thực sự xảy ra với mọi người mọi lúc. Không giống như hết dung lượng đĩa vì cols FK lớn hơn một chút

Cuối cùng, vấn đề lớn với ints là bạn hết chúng !!! ok trong lý thuyết bạn không, có tải. Nhưng trong thực tế, bạn làm bởi vì mọi người không coi chúng như những con số ngẫu nhiên không có ý nghĩa. họ làm những việc như

  • oh tôi không muốn khách hàng nghĩ rằng chúng tôi là người mới bắt đầu từ 10.000

  • Tôi đã phải nhập một tải dữ liệu vì vậy tôi chỉ cần tăng hạt giống lên 1m để chúng tôi biết những gì được nhập khẩu

  • chúng ta cần loại dữ liệu. mỗi kỳ bắt đầu từ một triệu tiếp theo để chúng ta có thể sử dụng các chữ số đầu tiên làm số ma thuật

  • Tôi đã xóa và nhập lại tất cả dữ liệu với id mới. Có ngay cả nhật ký kiểm toán.

  • sử dụng số này, là khóa tổng hợp, làm id của thứ khác


1
Thực tế không có gì sai với câu trả lời này, nhưng tôi sẽ (để tránh những lần hạ thấp hơn nữa) có thể nói rõ rằng, mặc dù các ứng dụng trong đời thực sẽ không gặp phải va chạm, nhưng về mặt lý thuyết là có thể. (Hoặc có lẽ 45+ cơ sở dữ liệu exabyte phổ biến hơn tôi nghĩ ...). Mặc dù tôi nghĩ rằng ngôn ngữ "lý do quan trọng nhất" hơi mạnh mẽ, đây là điều tôi thấy hữu ích nhất.
BurnsBA

2
nhiều khả năng một chiếc inc tự động sẽ va chạm hơn một hướng dẫn
Ewan

4
-1 cho "Bạn nên sử dụng các hướng dẫn theo mặc định trong bất kỳ ứng dụng nào." Nó phụ thuộc ™. Và như những người khác đã chỉ ra, GUID / UUID, hoàn toàn không được đảm bảo là duy nhất.
Max Vernon

3
Câu trả lời "Nó phụ thuộc" là vô ích, chắc chắn sẽ có một số ứng dụng kỳ lạ trong đó một int là tốt hơn. Nhưng rất có thể ứng dụng của bạn không phải là một trong số đó. HƯỚNG DẪN là điều độc đáo nhất bạn có thể nhận được
Ewan

2
Tôi nghĩ rằng sẽ có một số ứng dụng kỳ lạ trong đó các hướng dẫn tốt hơn. Độc đáo không phải là điều quan trọng nhất để xem xét. "Lỗ hổng" ints của bạn bị ồ ạt và bạn không xem xét bất kỳ nhược điểm nào của hướng dẫn.
Andy

2

Tôi nhận ra rằng các GUID này được sử dụng để xác định các đối tượng ở cấp ứng dụng. Có phải chúng cũng được lưu trữ như là khóa chính ở cấp cơ sở dữ liệu.

Đó là nơi bạn nên dừng lại, ngay tại đó và suy nghĩ lại.

Khóa chính cơ sở dữ liệu của bạn KHÔNG BAO GIỜ có ý nghĩa kinh doanh. Nó nên là vô nghĩa theo định nghĩa.

Vì vậy, hãy thêm GUID làm khóa doanh nghiệp của bạn và khóa chính thông thường (thường là int dài) làm khóa chính của cơ sở dữ liệu. Bạn luôn có thể đặt một chỉ mục duy nhất trên GUID để đảm bảo tính duy nhất.

Đó là lý thuyết cơ sở dữ liệu nói chuyện tất nhiên, nhưng nó cũng là thực hành tốt. Tôi đã xử lý các cơ sở dữ liệu trong đó các khóa chính có ý nghĩa kinh doanh (một khách hàng đã nghĩ sẽ lưu một số tài nguyên cơ sở dữ liệu bằng cách sử dụng chúng làm số nhân viên, số khách hàng, v.v.) và nó luôn dẫn đến rắc rối.


1
Làm thế nào khác với truy vấn từ lớp ứng dụng bằng khóa chính số nguyên? Tại thời điểm đó, nó cũng đang được sử dụng để xác định các đối tượng ở lớp ứng dụng. Bạn cần một cách để xác định các đối tượng trong cơ sở dữ liệu từ lớp ứng dụng.
icirellik

@icirellik khóa chính có nghĩa là để sử dụng nội bộ bởi cơ sở dữ liệu, để liên kết các bản ghi cha và con và những thứ tương tự. Nó KHÔNG có nghĩa là sử dụng theo logic ứng dụng, bạn sử dụng ID doanh nghiệp cho điều đó, như số hoặc tên sản phẩm.
jwenting

2

Luôn sử dụng cơ sở dữ liệu được tạo, tự động tăng khóa chính (PK).

Tại sao nên sử dụng tự động tăng thay vì GUID / UUID?

  • GUID (UUID) không ngăn chặn các va chạm chính vì chúng không phải là duy nhất và không có cách nào để làm cho chúng trở nên độc nhất vì chúng được tạo từ nhiều nguồn.
  • GUID không giúp hợp nhất vì chúng làm tăng đáng kể quá trình hợp nhất đã tốn thời gian với các cột PK và FK không nguyên, rất dài, cần rất nhiều thời gian để xử lý. Hãy nhớ rằng đối với hầu hết các PK, sẽ có ít nhất 1 bảng khác có ít nhất 2 khóa có cùng kích thước: đó là PK riêng và FK trở lại bảng đầu tiên. Tất cả phải được giải quyết trong một hợp nhất.

Nhưng làm thế nào để xử lý các mảnh vỡ, cụm, vv?

  • Tạo các PK nhiều cột được tạo thành từ các cột riêng biệt xác định từng phân đoạn / cụm / cơ sở dữ liệu / bất cứ thứ gì quản lý các khóa tăng tự động của chính nó. Ví dụ...

PK 3 cột cho bảng phân cụm có thể là ...

 DB | SH | KEY     |
----|----|---------|
 01 | 01 | 1234567 |

Nhưng còn ...?

  • Nhiều chuyến đi đến cơ sở dữ liệu - Hầu hết các ứng dụng không cần xác định duy nhất một bản ghi được tạo cho đến khi nó được chèn vào cơ sở dữ liệu vì luồng / phiên / bất cứ thứ gì chỉ hoạt động trên một lần. Nếu ứng dụng thực sự cần khả năng này, hãy sử dụng ứng dụng tạo PK tạm thời không được gửi đến cơ sở dữ liệu . Để cơ sở dữ liệu sau đó đặt PK tự động tăng lên trên hàng khi nó được chèn. Các phần chèn sẽ sử dụng PK tạm thời, trong khi các bản cập nhật và xóa sẽ sử dụng PK vĩnh viễn được chỉ định bởi cơ sở dữ liệu.

  • Hiệu suất - Máy tính có thể xử lý các số nguyên đơn giản nhanh hơn nhiều so với bất kỳ thứ gì khác do miền lớn hơn rất nhiều nếu các giá trị có thể có trên mỗi phần tử trong GUID (37) so với số nguyên (10). Cũng cần nhớ rằng mỗi ký tự trong GUID trước tiên phải được chuyển đổi thành số để được CPU thao tác.

Các lỗi phổ biến của các khóa chính PK chỉ có một mục đích ... để xác định hoàn toàn duy nhất một hàng trong bảng. Bất cứ điều gì khác là một lạm dụng quá phổ biến.

Phát hiện hồ sơ mất tích

  • Hồ sơ mất tích không thể được phát hiện bằng cách nhìn vào PK. Bless QA ít nhất là cố gắng đảm bảo chất lượng dữ liệu. Tuy nhiên, họ và các lập trình viên thiếu hiểu biết về cách các khóa trong các hệ thống cơ sở dữ liệu hiện đại được gán thường khiến họ hiểu nhầm rằng một số bị thiếu trong PK tự động tăng có nghĩa là thiếu dữ liệu. Nó không vì ...
  • Để thực hiện, các hệ thống cơ sở dữ liệu phân bổ các khối số theo 'chuỗi' (lô, phạm vi) để giảm thiểu các chuyến đi đến cơ sở dữ liệu thực tế trong bộ lưu trữ. Kích thước của các dãy số này thường nằm dưới sự kiểm soát của DBA nhưng có thể không điều chỉnh được trên cơ sở mỗi bảng.
  • Điểm đáng chú ý là ... các số không được sử dụng từ các chuỗi này không bao giờ được trả về cơ sở dữ liệu nên luôn có các khoảng trống trong các số PK.
  • Tại sao có những con số không sử dụng bạn yêu cầu? Bởi vì một loạt các hành động bảo trì cơ sở dữ liệu có thể khiến các chuỗi bị bỏ qua. Đây là những thứ như khởi động lại, tải lại số lượng lớn các bảng, một số loại phục hồi từ bản sao lưu và một số hoạt động khác.

Sắp xếp

  • Sắp xếp theo PK rất dễ bị lỗi vì hầu hết mọi người sẽ nghĩ rằng nó liệt kê các hàng theo thứ tự chúng được tạo và tương ứng với thời gian. Chủ yếu, nhưng không cần thiết.
  • Các công cụ cơ sở dữ liệu được tối ưu hóa để đạt hiệu suất tối đa và điều đó có thể có nghĩa là trì hoãn việc chèn kết quả của một giao dịch phức tạp kéo dài để chèn các giao dịch đơn giản ngắn, "ngoài luồng".

Bạn nghĩ gì về lược đồ bảng sao cho cột duy nhất là khóa chính tăng tự động do cơ sở dữ liệu tạo ra? Riêng các bảng không có khóa ngoại nhưng có khóa chính là khóa ngoại cho một số bảng có liên quan?
RibaldEddie

Tôi đã thêm nhiều hơn vào câu trả lời dọc theo những dòng đó. Câu trả lời ban đầu chưa đầy đủ do ứng dụng Android SE tôi đang treo. Tôi nghĩ rằng một bản viết lại chính của ứng dụng đang được phát triển.
DocSalvager

Vì vậy, theo quan điểm của bạn, một bảng có thể chứa bất kỳ số lượng hàng nào được lưu giống hệt nhau cho khóa chính tăng tự động của chúng không?
RibaldEddie

@RibaldEddie - Theo như những gì DB được thiết kế để cho phép ... hoàn toàn. Xóa là dễ dàng. Khi kịch bản của bạn xảy ra, tôi sẽ coi đó là một lỗi được sửa trong phần mềm và sau đó xóa một trong hai hàng. Trường hợp phổ biến hơn nhiều là hai bản ghi cho cùng một thứ với dữ liệu hơi khác nhau để chúng phải được hợp nhất. Nếu một cột trống trong một bản ghi và có giá trị trong một bản ghi khác, sự lựa chọn là hiển nhiên và có thể được tự động hóa. Thông thường datetimestamp có thể được sử dụng để phân xử hợp nhất tự động. Một số bản sao yêu cầu một người hoàn thành và xác minh hợp nhất dựa trên các quy tắc kinh doanh.
DocSalvager

1

Giống như bất cứ điều gì, có những lợi thế và bất lợi để làm điều này:

Tốt:

  1. Các khóa của bạn luôn có cùng độ dài (cơ sở dữ liệu rất lớn có thể có các khóa rất lớn)

  2. Tính duy nhất được đảm bảo khá nhiều - ngay cả khi bạn tạo chúng từ một hệ thống riêng biệt và / hoặc chưa đọc ID cuối cùng từ cơ sở dữ liệu

Những người xấu:

  1. Như đã đề cập rất nhiều ở trên - chỉ mục lớn hơn và lưu trữ dữ liệu.

  2. Bạn không thể đặt hàng bằng ID, bạn phải đặt hàng bằng thứ khác. Nhiều chỉ số hơn, có thể kém hiệu quả hơn.

  3. Chúng ít người đọc được. Số nguyên thường dễ phân tích, ghi nhớ và gõ cho mọi người. Sử dụng GUID làm ID trong mệnh đề WHERE trên nhiều bảng đã tham gia có thể khiến đầu bạn tan chảy.

Giống như mọi thứ, hãy sử dụng chúng khi thích hợp, đừng giáo điều - trong nhiều tình huống, số nguyên tự động tăng sẽ tốt hơn, đôi khi GUID rất tuyệt.


0

Có, bạn có thể sử dụng GUID làm khóa chính. Mặt trái là kích thước và sự phân mảnh nhanh chóng của chỉ số.

Trừ khi bạn cần tính duy nhất trên các cơ sở dữ liệu (ví dụ: một cụm) số nguyên được ưu tiên.


Các bộ tạo GUID có thể tạo ra cùng một GUID nhiều lần, trong đó có một lỗ hổng. Chúng có hay không phụ thuộc vào độ chi tiết của chúng, chủ yếu dựa vào khoảng thời gian giữa các đồng hồ tích tắc. Ví dụ: một trình tạo dựa trên đồng hồ chỉ có thể đánh dấu trên mỗi 100ms, dẫn đến 2 GUID được yêu cầu trong vòng 100ms trên máy đó giống hệt nhau. Có nhiều cách để tránh điều đó, chủ yếu, nhưng nhiều trình tạo GUID hoạt động hoàn toàn ngoài địa chỉ IP và / hoặc địa chỉ MAC và dấu thời gian.
jwenting

0

Đây là vấn đề của tôi về vấn đề này - giải pháp là một ngôi nhà nửa chừng giữa giá trị GUID và int, tận dụng tốt nhất cả hai.

Lớp tạo ra một giá trị Id giả ngẫu nhiên (nhưng tăng dần theo thời gian), tương tự như Comb GUID .

Ưu điểm chính là nó cho phép các giá trị Id được tạo trên máy khách, thay vì sử dụng các giá trị tăng tự động được tạo trên máy chủ (yêu cầu một chuyến đi khứ hồi) với rủi ro gần như bằng 0 của các giá trị trùng lặp.

Các giá trị được tạo chỉ sử dụng 8 byte thay vì 16 cho GUID và không phụ thuộc vào một thứ tự sắp xếp cơ sở dữ liệu cụ thể (ví dụ: Sql Server cho GUID ). Các giá trị có thể được mở rộng để sử dụng toàn bộ phạm vi dài không dấu, nhưng điều này sẽ gây ra sự cố với bất kỳ cơ sở dữ liệu hoặc kho lưu trữ dữ liệu nào khác chỉ có các kiểu số nguyên đã ký.

public static class LongIdGenerator
{
    // set the start date to an appropriate value for your implementation 
    // DO NOT change this once any application that uses this functionality is live, otherwise existing Id values will lose their implied date
    private static readonly DateTime PeriodStartDate = new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private static readonly DateTime PeriodEndDate = PeriodStartDate.AddYears(100);
    private static readonly long PeriodStartTicks = PeriodStartDate.Ticks;
    private static readonly long PeriodEndTicks = PeriodEndDate.Ticks;
    private static readonly long TotalPeriodTicks = PeriodEndTicks - PeriodStartTicks;

    // ensures that generated Ids are always positve
    private const long SEQUENCE_PART_PERMUTATIONS = 0x7FFFFFFFFFFF; 

    private static readonly Random Random = new Random();

    private static readonly object Lock = new object();
    private static long _lastSequencePart;

    public static long GetNewId()
    {
        var sequencePart = GetSequenceValueForDateTime(DateTime.UtcNow);

        // extra check, just in case we manage to call GetNewId() twice before enough ticks have passed to increment the sequence 
        lock (Lock)
        {
            if (sequencePart <= _lastSequencePart)
                sequencePart = _lastSequencePart + 1;

            _lastSequencePart = sequencePart;
        }

        // shift so that the sequence part fills the most significant 6 bytes of the result value
        sequencePart = (sequencePart << 16);

        // randomize the lowest 2 bytes of the result, just in case two different client PCs call GetNewId() at exactly the same time
        var randomPart = Random.Next() & 0xFFFF;

        return sequencePart + randomPart;
    }

    // used if you want to generate an Id value for a historic time point (within the start and end dates)
    // there are no checks, compared to calls to GetNewId(), but the chances of colliding values are still almost zero
    public static long GetIdForDateTime(DateTime dt)
    {
        if (dt < PeriodStartDate || dt > PeriodStartDate)
            throw new ArgumentException($"value must be in the range {PeriodStartDate:dd MMM yyyy} - {PeriodEndDate:dd MMM yyyy}");

        var sequencePart = GetSequenceValueForDateTime(dt.ToUniversalTime());
        var randomPart = Random.Next() & 0xFFFF;
        return ( sequencePart << 16 ) + randomPart;
    }

    // Get a 6 byte sequence value from the specified date time - startDate => 0 --> endDate => 0x7FFFFFFFFFFF
    // For a 100 year time period, 1 unit of the sequence corresponds to about 0.022 ms
    private static long GetSequenceValueForDateTime(DateTime dt)
    {
        var ticksFromStart = dt.ToUniversalTime().Ticks - PeriodStartTicks;
        var proportionOfPeriod = (decimal)ticksFromStart / TotalPeriodTicks;
        var result = proportionOfPeriod * SEQUENCE_PART_PERMUTATIONS;
        return (long)result;
    }

    public static DateTime GetDateTimeForId(long value)
    {
        // strip off the random part - the two lowest bytes
        var timePart = value >> 16;
        var proportionOfTotalPeriod = (decimal) timePart / SEQUENCE_PART_PERMUTATIONS;
        var ticks = (long)(proportionOfTotalPeriod * TotalPeriodTicks);
        var result = PeriodStartDate.AddTicks(ticks);
        return result;
    }
}
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.