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? 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:
Một số Ugliness với GUID
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 đó. Đ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 . 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:
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:
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. 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. 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.
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
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ư
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. 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?
Nhưng làm thế nào để xử lý các mảnh vỡ, cụm, vv?
PK 3 cột cho bảng phân cụm có thể là ... DB | SH | KEY |----|----|---------| 01 | 01 | 1234567 | Nhưng còn ...?
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
Sắp xếp
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:
Những người xấu:
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. 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. Đâ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; }} |