我应该如何在MySQL表中存储GUID?

我使用varchar(36)还是有更好的方法来做到这一点?

我的DBA问我什么时候询问为我的对象存储GUID的最佳方法,为什么我需要存储16个字节,而我可以用一个整数在4个字节中做同样的事情。 既然他把这个挑战摆在我面前,我认为现在是提及它的好时机。 话虽如此…

如果要优化使用存储空间,可以将GUID存储为CHAR(16)二进制文件。

我将它存储为char(36)。

添加到ThaBadDawg的答案,使用这些方便的function(感谢我的一个聪明的同事)从36长度的string返回到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) ,select你喜欢的味道

要更好地跟踪代码,请参考下面的数字sortingGUID示例。 (非法字符用于说明的目的 – 每个地方都有一个唯一的字符)。这些函数将转换字节顺序,以实现优先索引聚类的位顺序。 示例下方显示了重新sorting的guid。

 12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW 78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW 

删除的虚线:

 123456789ABCDEFGHIJKLMNOPQRSTUVW 78563412BC9AFGDEHIJKLMNOPQRSTUVW 

char(36)将是一个不错的select。 也可以使用MySQL的UUID()函数,它返回一个36字符的文本格式(hex连字符),可用于从数据库检索这样的ID。

“更好”取决于您正在优化的内容。

存储容量/性能与易用性有多大关系? 更重要的是 – 您是否生成了足够多的GUID,或者足够频繁地获取这些GUID?

如果答案是“否”,那么char(36)就足够好了,它使存储/获取GUID变得简单。 否则, binary(16)是合理的,但是你将不得不依靠MySQL和/或你select的编程语言来从通常的string表示来回转换。

二进制(16)会很好,比使用varchar(32)更好。

应该调整由KCD发布的GuidToBinary例程,以考虑GUIDstring中时间戳的位布局。 如果string表示版本1 UUID,就像uuid()mysql例程所返回的那样,那么时间分量被embedded字母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,因为big endian已经产生了最好的二进制索引字节顺序。 但是,您确实需要在低位字节前面移动最重要的字节。 因此,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的交换algorithm会把它放在第三个ID之前(直stream前3e,第一个块最后一个字节)。

索引的正确顺序是:

 1e497556eec5eb6... 1e497556eec5f10... 1e497556eec8ddc... 1e497556eee30d0... 1e497556efda038... 1e497556f9641bf... 1e49755758c3e3e... 

请参阅本文以获取支持信息: http : //mysql.rjweb.org/doc.php/uuid

***请注意,我不会从时间戳的高12位中分割半字节版本。 这是你的例子中的D半字节。 我只是把它扔在前面。 所以我的二进制序列最终是DEFG9ABC等。 这意味着我所有索引的UUID都以相同的半字节开头。 文章做同样的事情。

对于那些只是磕磕绊绊的人来说,按照Percona的研究,现在有更好的select。

它包括重组UUID块以获得最佳索引,然后转换为二进制以减less存储空间。

阅读完整的文章在这里

我build议使用下面的函数,因为@ bigh_29提到的函数将我的guid转换为新的函数(原因我不明白)。 而且,这些在我的桌子上做的testing中速度要快一些。 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)字段的比较/sorting/索引要比string快得多,而且在数据库中占用的空间也less两倍