Tôi có sử dụng varchar (36) hay có cách nào tốt hơn để làm điều đó không?
Tôi có sử dụng varchar (36) hay có cách nào tốt hơn để làm điều đó không?
Câu trả lời:
DBA của tôi đã hỏi tôi khi tôi hỏi về cách tốt nhất để lưu GUID cho các đối tượng của mình tại sao tôi cần lưu trữ 16 byte khi tôi có thể làm điều tương tự trong 4 byte với một Integer. Kể từ khi anh ấy đưa thử thách đó ra cho tôi, tôi nghĩ bây giờ là thời điểm tốt để đề cập đến nó. Điều đó đang được nói ...
Bạn có thể lưu trữ một hướng dẫn dưới dạng nhị phân CHAR (16) nếu bạn muốn sử dụng không gian lưu trữ tối ưu nhất.
Tôi sẽ lưu trữ nó như một char (36).
-
s.
Thêm vào câu trả lời của ThaBadDawg, sử dụng các hàm tiện dụng này (nhờ một đồng nghiệp khôn ngoan hơn của tôi) để lấy từ chuỗi 36 chiều dài trở lại một mảng 16 byte.
DELIMITER $$
CREATE FUNCTION `GuidToBinary`(
$Data VARCHAR(36)
) RETURNS binary(16)
DETERMINISTIC
NO SQL
BEGIN
DECLARE $Result BINARY(16) DEFAULT NULL;
IF $Data IS NOT NULL THEN
SET $Data = REPLACE($Data,'-','');
SET $Result =
CONCAT( UNHEX(SUBSTRING($Data,7,2)), UNHEX(SUBSTRING($Data,5,2)),
UNHEX(SUBSTRING($Data,3,2)), UNHEX(SUBSTRING($Data,1,2)),
UNHEX(SUBSTRING($Data,11,2)),UNHEX(SUBSTRING($Data,9,2)),
UNHEX(SUBSTRING($Data,15,2)),UNHEX(SUBSTRING($Data,13,2)),
UNHEX(SUBSTRING($Data,17,16)));
END IF;
RETURN $Result;
END
$$
CREATE FUNCTION `ToGuid`(
$Data BINARY(16)
) RETURNS char(36) CHARSET utf8
DETERMINISTIC
NO SQL
BEGIN
DECLARE $Result CHAR(36) DEFAULT NULL;
IF $Data IS NOT NULL THEN
SET $Result =
CONCAT(
HEX(SUBSTRING($Data,4,1)), HEX(SUBSTRING($Data,3,1)),
HEX(SUBSTRING($Data,2,1)), HEX(SUBSTRING($Data,1,1)), '-',
HEX(SUBSTRING($Data,6,1)), HEX(SUBSTRING($Data,5,1)), '-',
HEX(SUBSTRING($Data,8,1)), HEX(SUBSTRING($Data,7,1)), '-',
HEX(SUBSTRING($Data,9,2)), '-', HEX(SUBSTRING($Data,11,6)));
END IF;
RETURN $Result;
END
$$
CHAR(16)
thực sự là một BINARY(16)
, chọn hương vị ưa thích của bạn
Để làm theo mã tốt hơn, hãy lấy ví dụ đưa ra GUID theo thứ tự chữ số bên dưới. (Các ký tự bất hợp pháp được sử dụng cho mục đích minh họa - mỗi nơi đặt một ký tự duy nhất.) Các hàm sẽ chuyển đổi thứ tự byte để đạt được thứ tự bit cho phân cụm chỉ mục vượt trội. Hướng dẫn sắp xếp lại được hiển thị dưới ví dụ.
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW
Dấu gạch ngang bị xóa:
123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW
GuidToBinary
($ guide char (36)) TRẢ LẠI nhị phân (16) RETURN CONCAT (UNHEX (SUBSTRING ($ guide, 7, 2)), UNHEX (SUBSTRING ($) 5, 2)), UNHEX (SUBSTRING ($ guide, 3, 2)), UNHEX (SUBSTRING ($ guide, 1, 2)), UNHEX (SUBSTRING ($ guide, 12, 2)), UNHEX (SUBSTRING ($ guide, 10, 2)), UNHEX (SUBSTRING ($ guide, 17, 2)), UNHEX (SUBSTRING ($ guide, 15, 2)), UNHEX (SUBSTRING ($ guide, 20, 4)), UNHEX (SUBSTRING (hướng dẫn $, 25, 12)));
CHAR
và BINARY
tương đương ( các tài liệu dường như ngụ ý có sự khác biệt quan trọng và giải thích về hiệu suất chỉ số lý do tại sao nhóm là tốt hơn với byte sắp xếp lại.
"Tốt hơn" phụ thuộc vào những gì bạn đang tối ưu hóa.
Bao nhiêu bạn quan tâm về kích thước / hiệu suất lưu trữ so với dễ phát triển? Quan trọng hơn - bạn có tạo đủ GUID hoặc tìm nạp chúng thường xuyên đủ không, điều đó có quan trọng không?
Nếu câu trả lời là "không", char(36)
thì quá đủ tốt và nó làm cho việc lưu trữ / tìm nạp GUID trở nên đơn giản. Mặt khác, binary(16)
là hợp lý, nhưng bạn sẽ phải dựa vào MySQL và / hoặc ngôn ngữ lập trình bạn chọn để chuyển đổi qua lại từ cách biểu diễn chuỗi thông thường.
Thường trình GuidToBinary được đăng bởi KCD nên được điều chỉnh để tính đến bố cục bit của dấu thời gian trong chuỗi GUID. Nếu chuỗi đại diện cho UUID phiên bản 1, giống như chuỗi được trả về bởi thường trình mysu uuid (), thì các thành phần thời gian được nhúng trong các chữ cái 1-G, ngoại trừ D.
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
12345678 = least significant 4 bytes of the timestamp in big endian order
9ABC = middle 2 timestamp bytes in big endian
D = 1 to signify a version 1 UUID
EFG = most significant 12 bits of the timestamp in big endian
Khi bạn chuyển đổi thành nhị phân, thứ tự tốt nhất để lập chỉ mục sẽ là: EFG9ABC12345678D + phần còn lại.
Bạn không muốn trao đổi 12345678 thành 78563412 vì endian lớn đã mang lại thứ tự byte chỉ số nhị phân tốt nhất. Tuy nhiên, bạn muốn các byte quan trọng nhất được di chuyển trước các byte thấp hơn. Do đó, EFG đi trước, tiếp theo là bit giữa và bit thấp hơn. Tạo ra một tá UUID với uuid () trong vòng một phút và bạn sẽ thấy thứ tự này mang lại thứ hạng chính xác như thế nào.
select uuid(), 0
union
select uuid(), sleep(.001)
union
select uuid(), sleep(.010)
union
select uuid(), sleep(.100)
union
select uuid(), sleep(1)
union
select uuid(), sleep(10)
union
select uuid(), 0;
/* output */
6eec5eb6-9755-11e4-b981-feb7b39d48d6
6eec5f10-9755-11e4-b981-feb7b39d48d6
6eec8ddc-9755-11e4-b981-feb7b39d48d6
6eee30d0-9755-11e4-b981-feb7b39d48d6
6efda038-9755-11e4-b981-feb7b39d48d6
6f9641bf-9755-11e4-b981-feb7b39d48d6
758c3e3e-9755-11e4-b981-feb7b39d48d6
Hai UUID đầu tiên được tạo ra gần nhất theo thời gian. Chúng chỉ khác nhau trong 3 ngòi cuối cùng của khối đầu tiên. Đây là các bit có ý nghĩa ít nhất của dấu thời gian, có nghĩa là chúng ta muốn đẩy chúng sang phải khi chúng ta chuyển đổi nó thành một mảng byte có thể lập chỉ mục. Như một ví dụ ngược lại, ID cuối cùng là mới nhất, nhưng thuật toán hoán đổi của KCD sẽ đặt nó trước ID thứ 3 (3e trước dc, các byte cuối từ khối đầu tiên).
Thứ tự đúng để lập chỉ mục sẽ là:
1e497556eec5eb6...
1e497556eec5f10...
1e497556eec8ddc...
1e497556eee30d0...
1e497556efda038...
1e497556f9641bf...
1e49755758c3e3e...
Xem bài viết này để biết thông tin hỗ trợ: http://mysql.rjweb.org/doc.php/uuid
*** lưu ý rằng tôi không tách phiên bản nibble từ 12 bit cao của dấu thời gian. Đây là D nibble từ ví dụ của bạn. Tôi chỉ cần ném nó ở phía trước. Vì vậy, chuỗi nhị phân của tôi kết thúc là DEFG9ABC và cứ thế. Điều này ngụ ý rằng tất cả các UUID được lập chỉ mục của tôi bắt đầu với cùng một ngòi nổ. Bài báo làm điều tương tự.
Đối với những người chỉ vấp phải điều này, giờ đây đã có một sự thay thế tốt hơn nhiều theo nghiên cứu của Percona.
Nó bao gồm việc sắp xếp lại các khối UUID để lập chỉ mục tối ưu, sau đó chuyển đổi thành nhị phân để giảm dung lượng.
Đọc toàn bộ bài báo ở đây
Tôi sẽ đề nghị sử dụng các hàm bên dưới vì các hàm được đề cập bởi @ bigh_29 chuyển đổi các hướng dẫn của tôi thành các hàm mới (vì lý do tôi không hiểu). Ngoài ra, đây là nhanh hơn một chút trong các thử nghiệm tôi đã làm trên bàn của mình. https://gist.github.com/damienb/159151
DELIMITER |
CREATE FUNCTION uuid_from_bin(b BINARY(16))
RETURNS CHAR(36) DETERMINISTIC
BEGIN
DECLARE hex CHAR(32);
SET hex = HEX(b);
RETURN LOWER(CONCAT(LEFT(hex, 8), '-', MID(hex, 9,4), '-', MID(hex, 13,4), '-', MID(hex, 17,4), '-', RIGHT(hex, 12)));
END
|
CREATE FUNCTION uuid_to_bin(s CHAR(36))
RETURNS BINARY(16) DETERMINISTIC
RETURN UNHEX(CONCAT(LEFT(s, 8), MID(s, 10, 4), MID(s, 15, 4), MID(s, 20, 4), RIGHT(s, 12)))
|
DELIMITER ;
nếu bạn có giá trị char / varchar được định dạng là GUID tiêu chuẩn, bạn chỉ cần lưu trữ nó dưới dạng BINary (16) bằng cách sử dụng CAST đơn giản (MyString AS BINARY16), mà không cần tất cả các chuỗi khó hiểu của CONCAT + SUBSTR.
Các trường BINary (16) được so sánh / sắp xếp / lập chỉ mục nhanh hơn nhiều so với các chuỗi và cũng chiếm ít hơn hai lần không gian trong cơ sở dữ liệu
select CAST("hello world, this is as long as uiid" AS BINARY(16));
sản xuấthello world, thi