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

时间:2009-01-05 05:37:13

标签: mysql guid uuid

我是否使用varchar(36)或有更好的方法吗?

10 个答案:

答案 0 :(得分:97)

当我询问有关为我的对象存储GUID的最佳方法时,我的DBA问我,为什么我需要存储16个字节,而我可以使用Integer以4个字节执行相同的操作。既然他把那个挑战放在我身边,我想现在是提及它的好时机。那就是说......

如果想要最佳地利用存储空间,可以将guid存储为CHAR(16)二进制文件。

答案 1 :(得分:41)

我会将它存储为char(36)。

答案 2 :(得分:31)

添加到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

删除了Dashes:

123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW

答案 3 :(得分:24)

char(36)将是一个不错的选择。此外,还可以使用MySQL的UUID()函数,该函数返回36个字符的文本格式(带连字符的十六进制),可用于从数据库中检索此类ID。

答案 4 :(得分:17)

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

您对存储容量/性能与易于开发的关注程度有多大?更重要的是 - 您是否生成足够的GUID,或者经常提取它们,这很重要吗?

如果答案是“否”,char(36)就足够了,它使存储/获取GUID变得简单。否则,binary(16)是合理的,但您必须依靠MySQL和/或您选择的编程语言来转换来自通常的字符串表示。

答案 5 :(得分:8)

二进制(16)没问题,比使用varchar(32)更好。

答案 6 :(得分:7)

应调整KCD发布的GuidToBinary例程,以考虑GUID字符串中时间戳的位布局。如果字符串表示版本1 UUID,就像uuid()mysql例程返回的那样,则时间组件嵌入字母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的交换算法会将它放在第3个ID之前(直流前3e,第一个块的最后一个字节)。

索引的正确顺序是:

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

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

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

答案 7 :(得分:5)

对于那些刚刚陷入困境的人来说,Percona的研究现在有了更好的选择。

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

阅读完整文章here

答案 8 :(得分:1)

我建议使用下面的功能,因为@ bigh_29提到的功能会将我的guids转换为新的功能(原因我不明白)。而且,在我在桌子上进行的测试中,这些速度要快一些。 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 ;

答案 9 :(得分:-4)

如果你有一个char / varchar值格式化为标准GUID,你可以使用简单的CAST(MyString AS BINARY16)将它存储为BINARY(16),而不是所有那些令人难以置信的CONCAT + SUBSTR序列。 / p>

BINARY(16)字段的比较/排序/索引比字符串快得多,并且数据库中的空间也减少了两倍