Có bị phạt khi sử dụng BINary (16) thay vì UNIQUEIDENTIFIER không?


19

Gần đây tôi đã kế thừa cơ sở dữ liệu SQL Server sử dụng BINARY(16)thay vì UNIQUEIDENTIFIERlưu trữ Hướng dẫn. Nó làm điều này cho tất cả mọi thứ bao gồm các khóa chính.

Tôi có nên quan tâm không?


Nó sử dụng nhị phân (16) nhất quán xuyên suốt? Bao gồm các biến và tham số? Nếu không bạn cần xem xét ảnh hưởng của các diễn viên ngầm.
Martin Smith

Vâng, rất may tôi cũng không phải đối phó với các diễn viên ngầm.
Jonathan Allen

Câu trả lời:


21

Tôi có nên quan tâm không?

Vâng, có một vài điều ở đây có một chút liên quan.

Đầu tiên: mặc dù đúng UNIQUEIDENTIFIER(tức là Guid) là giá trị nhị phân 16 byte, nhưng cũng đúng:

  1. Tất cả dữ liệu có thể được lưu trữ ở dạng nhị phân (ví dụ: INTcó thể được lưu trữ trong BINARY(4), DATETIMEcó thể được lưu trữ trong BINARY(8), v.v.), do đó # 2
  2. Có lẽ có một lý do để có một kiểu dữ liệu riêng cho GUID ngoài sự tiện lợi đơn thuần (ví dụ sysnamenhư bí danh cho NVARCHAR(128)).

Ba khác biệt về hành vi mà tôi có thể tìm thấy là:

  • So sánh UNIQUEIDENTIFIERcác giá trị trong SQL Server, tốt hơn hay tồi tệ hơn, thực tế không được thực hiện giống như so sánhBINARY(16) các giá trị. Theo trang MSDN để so sánh các giá trị GUID và định danh duy nhất , khi so sánh UNIQUEIDENTIFIERcác giá trị trong SQL Server:

    sáu byte cuối cùng của một giá trị là quan trọng nhất

  • Mặc dù các giá trị này không được sắp xếp thường xuyên, có một chút khác biệt giữa hai loại này. Theo trang MSDN cho uniqueidentifier :

    thứ tự không được thực hiện bằng cách so sánh các mẫu bit của hai giá trị.

  • Do có sự khác biệt về cách xử lý các giá trị GUID giữa SQL Server và .NET (được lưu ý trong trang "So sánh giá trị GUID và giá trị định danh duy nhất" được liên kết ở trên), việc kéo dữ liệu này ra khỏi SQL Server vào mã ứng dụng có thể không được xử lý đúng trong mã ứng dụng nếu cần mô phỏng hành vi so sánh SQL Server. Hành vi đó có thể được mô phỏng bằng cách chuyển đổi thành a SqlGuid, nhưng liệu nhà phát triển có biết làm điều đó không?

Thứ hai: dựa trên tuyên bố sau

Nó làm điều này cho tất cả mọi thứ bao gồm các khóa chính.

Nói chung, tôi sẽ quan tâm đến hiệu năng hệ thống bằng cách sử dụng GUID làm PK thay vì làm Khóa thay thế cùng với sử dụng INThoặc thậm chí BIGINTlà PK. Và thậm chí quan tâm hơn nếu các PK GUID này là các Chỉ mục được nhóm.

CẬP NHẬT

Nhận xét sau đây, được OP đưa ra về câu trả lời của @ Rob, đưa ra một mối quan tâm bổ sung:

nó đã được di chuyển từ tôi nghĩ rằng MySQL

GUID có thể được lưu trữ ở 2 định dạng nhị phân khác nhau . Vì vậy, có thể có lý do cho mối quan tâm tùy thuộc vào:

  1. đại diện nhị phân được tạo ra trên hệ thống nào và
  2. nếu các giá trị chuỗi được sử dụng bên ngoài hệ thống ban đầu, chẳng hạn như trong mã ứng dụng hoặc được cung cấp cho khách hàng để sử dụng trong các tệp nhập, v.v.

Vấn đề về nơi biểu diễn nhị phân được tạo ra phải liên quan đến thứ tự byte của 3 trong số 4 "trường" đầu tiên. Nếu bạn theo liên kết ở trên với bài viết Wikipedia, bạn sẽ thấy RFC 4122 chỉ định sử dụng mã hóa "Big Endian" cho tất cả 4 trường, nhưng Microsoft GUID chỉ định sử dụng Endianness "Bản địa". Chà, kiến ​​trúc Intel là Little Endian, do đó thứ tự byte cho 3 trường đầu tiên được đảo ngược từ các hệ thống theo RFC (cũng như GUID kiểu Microsoft được tạo trên các hệ thống Big Endian). Trường đầu tiên, "Dữ liệu 1", là 4 byte. Trong một Endianness, nó sẽ được biểu diễn dưới dạng (giả thuyết) 0x01020304. Nhưng trong Endianness khác nó sẽ được 0x04030201. Vì vậy, nếu cơ sở dữ liệu hiện tại 'BINARY(16)biểu diễn nhị phân được tạo trên một hệ thống theo RFC, sau đó chuyển đổi dữ liệu hiện tại trong BINARY(16)trường thành một UNIQUEIDENTIFIERkết quả sẽ dẫn đến một GUID khác với những gì ban đầu được tạo. Điều này không thực sự gây ra vấn đề NẾU các giá trị không bao giờ rời khỏi cơ sở dữ liệu các giá trị chỉ được so sánh với sự bình đẳng và không theo thứ tự.

Mối quan tâm với việc đặt hàng chỉ đơn giản là họ sẽ không theo cùng một trật tự sau khi chuyển đổi sang UNIQUEIDENTIFIER. May mắn thay, nếu hệ thống ban đầu thực sự là MySQL thì việc đặt hàng không bao giờ được thực hiện trên biểu diễn nhị phân ở vị trí đầu tiên vì MySQL chỉ có một đại diện chuỗi của UUID .

Mối quan tâm với các giá trị chuỗi được sử dụng bên ngoài cơ sở dữ liệu lại nghiêm trọng hơn, nếu biểu diễn nhị phân được tạo bên ngoài Windows / SQL Server. Vì thứ tự byte có khả năng khác nhau, nên cùng một GUID ở dạng chuỗi sẽ dẫn đến 2 biểu diễn nhị phân khác nhau, tùy thuộc vào nơi chuyển đổi đó diễn ra. Nếu mã ứng dụng hoặc khách hàng được cung cấp GUID ở dạng chuỗi ABCxuất phát từ dạng nhị phân 123 biểu diễn nhị phân được tạo trên hệ thống theo RFC, thì biểu diễn nhị phân đó (nghĩa là 123) sẽ chuyển thành dạng chuỗi DEFkhi được chuyển đổi thành a UNIQUEIDENTIFIER. Tương tự, dạng chuỗi ban đầu của ABCsẽ chuyển đổi thành dạng nhị phân 456khi được chuyển đổi thành a UNIQUEIDENTIFIER.

Vì vậy, nếu GUID không bao giờ rời khỏi cơ sở dữ liệu thì sẽ không có gì phải lo lắng về việc đặt hàng. Hoặc, nếu việc nhập từ MySQL được thực hiện bằng cách chuyển đổi dạng chuỗi (nghĩa là FCCEC3D8-22A0-4C8A-BF35-EC18227C9F40) thì có thể ổn. Khác, nếu những GUID đó được trao cho khách hàng hoặc trong mã ứng dụng, bạn có thể kiểm tra xem họ chuyển đổi bằng cách lấy một cái và chuyển đổi qua SELECT CONVERT(UNIQUEIDENTIFIER, 'value found outside of the database');và xem bạn có tìm thấy bản ghi dự kiến ​​không. Nếu bạn không thể khớp các bản ghi thì bạn có thể phải giữ các trường như BINARY(16).

Trong tất cả khả năng sẽ không có vấn đề gì, nhưng tôi đang đề cập đến vấn đề này bởi vì trong những điều kiện phù hợp có thể có một vấn đề.

Và làm thế nào để GUID mới được chèn? Tạo trong mã ứng dụng?

CẬP NHẬT 2

Nếu lời giải thích trước đây về vấn đề tiềm ẩn liên quan đến việc nhập các biểu diễn nhị phân của GUID được tạo trên một hệ thống khác là một chút (hoặc rất nhiều) khó hiểu, hy vọng những điều sau đây sẽ rõ ràng hơn một chút:

DECLARE @GUID UNIQUEIDENTIFIER = NEWID();
SELECT @GUID AS [String], CONVERT(BINARY(16), @GUID) AS [Binary];
-- String = 5FED23BE-E52C-40EE-8F45-49664C9472FD
-- Binary = 0xBE23ED5F2CE5EE408F4549664C9472FD
--          BE23ED5F-2CE5-EE40-8F45-49664C9472FD

Trong đầu ra được hiển thị ở trên, các giá trị "Chuỗi" và "Nhị phân" là từ cùng một GUID. Giá trị bên dưới dòng "Nhị phân" có cùng giá trị với dòng "Nhị phân", nhưng được định dạng theo cùng kiểu với dòng "Chuỗi" (nghĩa là đã xóa "0x" và thêm bốn dấu gạch ngang). So sánh các giá trị thứ nhất và thứ ba, chúng không hoàn toàn giống nhau, nhưng chúng rất gần nhau: hai phần bên phải giống hệt nhau, nhưng ba phần bên trái nhất thì không. Nhưng nếu bạn nhìn kỹ, bạn có thể thấy rằng đó là cùng một byte trong mỗi ba phần, chỉ theo một thứ tự khác nhau. Có thể dễ dàng hơn để xem nếu tôi chỉ hiển thị ba phần đầu tiên đó và đánh số byte để dễ dàng thấy thứ tự của chúng khác nhau như thế nào giữa hai biểu diễn:

Chuỗi = 1 5F 2 ED 3 23 4 BE - 5 E5 6 2C - 7 40 8 EE
Nhị phân = 4 BE 3 23 2 ED 1 5F - 6 2C 5 E5 - 8 EE 7 40 (trong Windows / SQL Server)

Vì vậy, trong mỗi nhóm, thứ tự của các byte được đảo ngược, nhưng chỉ trong Windows và cả SQL Server. Tuy nhiên, trên một hệ thống tuân thủ RFC, biểu diễn nhị phân sẽ phản ánh biểu diễn sting vì sẽ không có bất kỳ sự đảo ngược nào của thứ tự byte.

Dữ liệu được đưa vào SQL Server từ MySQL như thế nào? Dưới đây là một vài lựa chọn:

SELECT CONVERT(BINARY(16), '5FED23BE-E52C-40EE-8F45-49664C9472FD'),
       CONVERT(BINARY(16), 0x5FED23BEE52C40EE8F4549664C9472FD),
    CONVERT(BINARY(16), CONVERT(UNIQUEIDENTIFIER, '5FED23BE-E52C-40EE-8F45-49664C9472FD'));

Trả về:

0x35464544323342452D453532432D3430  
0x5FED23BEE52C40EE8F4549664C9472FD  
0xBE23ED5F2CE5EE408F4549664C9472FD

Giả sử nó là nhị phân thẳng thành nhị phân (tức là Chuyển đổi số 2 ở trên), thì GUID kết quả, nếu được chuyển đổi thành thực tế UNIQUEIDENTIFIER, sẽ là:

SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);

Trả về:

BE23ED5F-2CE5-EE40-8F45-49664C9472FD

Sai chỗ nào. Và điều đó khiến chúng ta có ba câu hỏi:

  1. Dữ liệu được nhập vào SQL Server như thế nào?
  2. Mã ứng dụng được viết bằng ngôn ngữ nào?
  3. Mã ứng dụng đang chạy trên nền tảng nào?

Tôi sẽ giả sử GUID được tạo trong ứng dụng, vì tôi không thấy chúng trong cơ sở dữ liệu.
Jonathan Allen

Tôi không thể nói rằng tôi hoàn toàn làm theo lời giải thích về thứ tự byte, nhưng nó khiến tôi suy nghĩ về việc lập chỉ mục. Định danh duy nhất sẽ ít nhiều có khả năng dẫn đến phân mảnh chỉ mục hơn nhị phân?
Jonathan Allen

2
@Jonathan ALLen Tôi đã thêm một phần CẬP NHẬT khác để hy vọng giải thích rõ hơn. Và không, lập chỉ mục không nên khác biệt giữa chúng.
Solomon Rutzky

"Rất may", SQL Server không thay đổi thứ tự giữa Biến thể 1 và Biến thể 2 - ngay cả khi 'có thể' được lưu trữ khác nhau trên đĩa, đó là cùng một thứ tự gây nhầm lẫn.
dùng2864740

5

Bạn luôn có thể quan tâm. ;)

Hệ thống có thể đã được di chuyển từ một số hệ thống khác không hỗ trợ định danh duy nhất. Có những thỏa hiệp nào khác mà bạn không biết?

Nhà thiết kế có thể không biết về loại định danh duy nhất. Những điều khác họ không biết về?

Về mặt kỹ thuật - nó không phải là một mối quan tâm chính.


Vâng, nó đã được di chuyển từ tôi nghĩ MySQL. Và vâng, có rất nhiều ... điều thú vị để xem xét.
Jonathan Allen
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.