Answers:
当我问到为对象存储GUID的最佳方法时,我的DBA问我为什么当我可以用整数将4字节的内容做同样的事情时,为什么需要存储16字节的内容。自从他向我提出挑战以来,我认为现在是提起挑战的好时机。话虽如此...
如果要最大程度地利用存储空间,可以将GUId存储为CHAR(16)二进制文件。
我会将其存储为char(36)。
-
。
除了ThaBadDawg的答案之外,还可以使用这些方便的函数(由于我的聪明的同事)将36个长度的字符串返回到16个字节数组。
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)
实际上是BINARY(16)
,请选择您喜欢的口味
为了更好地遵循代码,请使用下面给出了数字顺序GUID的示例。(非法字符用于说明目的-每个位置都有一个唯一字符。)这些函数将转换字节顺序,以实现高级索引聚类的位顺序。示例下面显示了重新排序的GUID。
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW
短划线已删除:
123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW
GuidToBinary
($ guid char(36))RETURNS binary(16)RETURN CONCAT(UNHEX(SUBSTRING($ guid,7,2)),UNHEX(SUBSTRING($ guid, 5,2)),UNHEX(SUBSTRING($ guid,3,2)),UNHEX(SUBSTRING($ guid,1,2)),UNHEX(SUBSTRING($ guid,12,2)),UNHEX(SUBSTRING($ guid,10,2)),UNHEX(SUBSTRING($ guid,17,2)),UNHEX(SUBSTRING($ guid,15,2)),UNHEX(SUBSTRING($ guid,20,4)),UNHEX(SUBSTRING ($ guid,25,12)));
“更好”取决于您要优化的内容。
您在乎存储大小/性能与易于开发之间有多少关系?更重要的是-您是否生成足够的GUID或足够频繁地获取它们,这很重要?
如果答案为“否”,char(36)
那就足够了,这会使存储/获取GUID非常简单。否则,这binary(16)
是合理的,但是您必须依靠MySQL和/或您选择的编程语言来从通常的字符串表示形式来回转换。
Binary(16)会很好,比使用varchar(32)更好。
应该对KCD发布的GuidToBinary例程进行调整,以考虑到GUID字符串中时间戳的位布局。如果该字符串表示版本1 UUID,如uuid()mysql例程返回的UUID,则时间分量将嵌入到字母1-G中,但不包括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
当您转换为二进制文件时,索引的最佳顺序为:EFG9ABC12345678D +其余部分。
您不希望将12345678交换为78563412,因为大端已产生最佳的二进制索引字节顺序。但是,您确实希望将最高有效字节移到较低字节的前面。因此,EFG首先进入,然后是中间位和低位。用一分钟的时间用uuid()生成十几个UUID,您应该看到此顺序如何产生正确的排名。
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
前两个UUID的生成时间最接近。它们仅在第一个块的最后3个半字节中变化。这些是时间戳的最低有效位,这意味着当我们将其转换为可索引的字节数组时,我们希望将其向右推。作为反例,最后一个ID是最新的,但是KCD的交换算法会将其放在第三个ID之前(dc之前的3e,第一个块的最后一个字节)。
正确的索引顺序为:
1e497556eec5eb6...
1e497556eec5f10...
1e497556eec8ddc...
1e497556eee30d0...
1e497556efda038...
1e497556f9641bf...
1e49755758c3e3e...
请参阅本文以获取支持信息:http : //mysql.rjweb.org/doc.php/uuid
***请注意,我不会从时间戳的高12位中拆分版本半字节。这是您示例中的D小节。我只是把它放在前面。所以我的二进制序列最终是DEFG9ABC,依此类推。这意味着我所有索引的UUID都以相同的半字节开头。这篇文章做同样的事情。
我建议使用以下功能,因为@ bigh_29提到的功能会将我的向导转换为新的向导(出于我不了解的原因)。而且,在我对表进行的测试中,这些速度要快一些。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 ;
如果您将char / varchar值格式设置为标准GUID,则可以使用简单的CAST(MyString AS BINARY16)将其存储为BINARY(16),而无需担心CONCAT + SUBSTR的所有麻烦。
与字符串相比,对BINARY(16)字段进行比较/排序/索引的速度要快得多,并且在数据库中占用的空间也要少两倍
select CAST("hello world, this is as long as uiid" AS BINARY(16));
生产hello world, thi