158

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

4

10 回答 10

115

当我询问为我的对象存储 GUID 的最佳方法时,我的 DBA 问我为什么我需要存储 16 个字节,而我可以用一个整数在 4 个字节中做同样的事情。自从他向我提出了这个挑战,我认为现在是提及它的好时机。话虽如此...

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

于 2009-01-05T05:44:13.943 回答
53

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

于 2009-01-05T05:41:27.227 回答
33

添加到 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
于 2011-08-23T23:56:28.597 回答
28

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

于 2009-01-05T05:47:58.647 回答
24

“更好”取决于您优化的目标。

您有多关心存储大小/性能与开发的难易程度?更重要的是 - 您是否生成了足够多的 GUID,或者是否足够频繁地获取它们,这很重要?

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

于 2014-02-28T16:26:30.760 回答
8

Binary(16) 会很好,比使用 varchar(32) 更好。

于 2012-05-31T12:00:16.160 回答
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,因为大端已经产生了最佳的二进制索引字节顺序。但是,您确实希望将最重要的字节移到较低字节的前面。因此,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 之前(3e 在 dc 之前,第一个块的最后一个字节)。

索引的正确顺序是:

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

有关支持信息,请参阅本文:http: //mysql.rjweb.org/doc.php/uuid

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

于 2015-01-08T16:56:31.960 回答
5

根据 Percona 的研究,对于那些刚刚遇到这个问题的人来说,现在有一个更好的选择。

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

在此处阅读全文

于 2015-06-26T14:24:21.047 回答
2

我建议使用下面的函数,因为@bigh_29 提到的函数将我的 guid 转换为新的(出于我不明白的原因)。此外,在我在桌子上进行的测试中,这些速度要快一些。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 ;
于 2015-12-17T15:22:12.667 回答
-4

如果您有一个格式化为标准 GUID 的 char/varchar 值,则可以使用简单的 CAST(MyString AS BINARY16) 将其简单地存储为 BINARY(16),而无需使用所有令人难以置信的 CONCAT + SUBSTR 序列。

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

于 2014-11-04T21:13:14.327 回答