将UUID v4存储在MySQL中

时间:2017-03-27 20:47:22

标签: mysql uuid

我根据找到的函数here

使用PHP生成UUID

现在我想将它存储在MySQL数据库中。存储UUID v4的最佳/最有效的MySQL字段格式是什么?

我目前有varchar(256),但我非常确定它比必要的要大得多。我发现了很多差不多的答案,但他们对于他们指的是什么形式的UUID一般都很模糊,所以我要求提供具体的格式。

7 个答案:

答案 0 :(得分:26)

如果您想要精确匹配,请将其存储为VARCHAR(36),或者VARCHAR(255)以相同的存储成本计算出来。这里没有理由对字节大惊小怪。

请记住,VARCHAR字段可变长度,因此存储成本与实际数据的数量成正比,而不是数据中的数据量。

将其存储为BINARY非常烦人,这些值无法打印,并且在运行查询时可能会显示为垃圾。很少有理由使用文字二进制表示。人类可读的值可以复制粘贴,并且可以轻松处理。

其他一些平台,如Postgres,有一个合适的UUID列,它在内部以更紧凑的格式存储它,但将其显示为人类可读的,因此您可以获得两种方法中的最佳效果。

答案 1 :(得分:8)

问题是关于在MySQL中存储UUID。

从mySQL 8.0版开始,您可以使用binary(16)通过UUID_TO_BIN/BIN_TO_UUID函数自动转换: https://mysqlserverteam.com/mysql-8-0-uuid-support/

请注意,mySQL还有一种快速生成UUID作为主键的方法:

  

INSERT INTO VALUES(UUID_TO_BIN(UUID(),true))

答案 2 :(得分:7)

如果您每行都有一个UUID,则可以将其存储为CHAR(36)并在VARCHAR(36)上每行保存1个字节。

uuid CHAR(36) CHARACTER SET ascii
  

与CHAR相比,VARCHAR值存储为1字节或2字节   长度前缀加数据。长度前缀表示数量   值中的字节数。如果值不需要,则列使用一个长度字节   超过255个字节,如果值可能需要多于两个长度字节   255个字节。   https://dev.mysql.com/doc/refman/5.7/en/char.html

虽然小心使用CHAR,但即使字段留空,也会始终使用定义的全长。另外,请务必使用ASCII作为字符集,因为CHAR会计划最坏情况(即utf8中每个字符3个字节,utf8mb4中4个字段)

  

[...] MySQL必须为CHAR中的每个字符保留四个字节   CHARACTER设置utf8mb4列,因为这是最大可能的   长度。例如,MySQL必须为CHAR保留40个字节(10)   字符集utf8mb4列。   https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html

答案 3 :(得分:1)

最有效的肯定是BINARY(16),存储人类可读的字符会占用两倍的存储空间,这意味着更大的索引和更慢的查找。如果您的数据足够小,不能以文本形式存储,就不会影响性能,那么您可能不需要无聊的整数键的UUID。存储原始数据实际上并不像其他人所建议的那样痛苦,因为任何体面的db admin工具都会将八位位组显示/转储为十六进制,而不是“文本”的原义字节。您无需在数据库中手动查找UUID。如果需要,HEX()x'deadbeef01'文字是您的朋友。在您的应用程序中编写一个函数(就像您引用的函数一样)来为您解决这个问题很简单。您甚至可以在数据库中将其作为虚拟列和存储过程来执行,因此该应用程序永远不会打扰原始数据。

我将UUID生成逻辑与显示逻辑分开,以确保不会更改现有数据并且可检测到错误:

function guidv4($prettify = false)
{
    static $native = function_exists('random_bytes');
    $data = $native ? random_bytes(16) : openssl_random_pseudo_bytes(16);

    $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
    $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10

    if ($prettify) {
        return guidv4_pretty($data);
    }
    return $data;
}

function guidv4_pretty($data)
{
    return strlen($data) == 16 ?
        vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)) :
        false;
}

function guidv4_ugly($data)
{
    $data = preg_replace('/[^\\dA-F]+/i', '', $data);
    return strlen($data) == 32 ? hex2bin($data) : false;
}

编辑:如果在读取数据库时只需要漂亮的列,则如下所示的语句就足够了:

ALTER TABLE test ADD uuid_pretty CHAR(36) GENERATED ALWAYS AS (CONCAT_WS('-', LEFT(HEX(uuid_ugly), 8), SUBSTR(HEX(uuid_ugly), 9, 4), SUBSTR(HEX(uuid_ugly), 13, 4), SUBSTR(HEX(uuid_ugly), 17, 4), RIGHT(HEX(uuid_ugly), 12))) VIRTUAL;

答案 4 :(得分:1)

最节省空间的是BINARY(16)或两个BIGINT UNSIGNED

前者可能使您头疼,因为手动查询不会(以直接的方式)为您提供可读/可复制的值。 后者可能使您头痛,因为必须在一个值和两个列之间进行映射。

如果这是主键,那么我绝对不会浪费任何空间,因为它也成为每个二级索引的一部分。换句话说,我将选择这些类型之一。

为了提高性能,随机UUID(即UUID v4,它是随机的)的随机性将严重损害。当UUID是您的主键或对它进行很多范围查询时,这适用。您在主索引中的插入将遍及整个位置,而不是在末尾(或附近)全部插入。您的数据丢失了时间局部性,这在各种情况下都是有用的属性。

我的主要改进是使用与UUID v1类似的方法,该方法使用时间戳作为其数据的一部分,并确保时间戳位于最高位。例如,UUID可能由以下内容组成:

Timestamp | Machine Identifier | Counter

这样,我们得到的局部性类似于自动增量值。

答案 5 :(得分:1)

我刚刚找到了一篇不错的文章,其中涉及以下主题:https://www.xaprb.com/blog/2009/02/12/5-ways-to-make-hexadecimal-identifiers-perform-better-on-mysql/

它涵盖了值的存储,并在此页面的不同答案中已经表达了相同的选项:

  • 一个:当心字符集
  • 二:使用定长,不可为空的值
  • 三:使其成为二进制文件

而且还增加了一些有关索引的有趣见解:

  • 四:使用前缀索引
  

在许多(但不是全部)情况下,您无需索引   价值。我通常发现前8到10个字符是   独特。如果它是二级索引,通常就足够了。的   这种方法的优点是您可以将其应用于现有   应用程序,而无需将列修改为BINARY或   其他任何事情-这是仅索引的更改,不需要   应用程序或要更改的查询。

请注意,本文并未告诉您如何创建这样的“前缀”索引。查看Column Indexes的MySQL文档,我们发现:

  

[...]您可以创建一个仅使用索引的前N个字符的索引   柱。以这种方式仅索引列值的前缀可以使   索引文件要小得多。当您为BLOB或TEXT列建立索引时,   必须为索引指定前缀长度。例如:

     

CREATE TABLE test (blob_col BLOB, INDEX(blob_col(10)));

     

[...]中的前缀长度   CREATE TABLE,ALTER TABLE和CREATE INDEX语句被解释   作为非二进制字符串类型(CHAR,VARCHAR,   TEXT)和二进制字符串类型(BINARY,VARBINARY,   BLOB)。

  • 五:构建哈希索引
  

您可以做的是生成值的校验和并对其进行索引。   是的,一个哈希哈希。在大多数情况下,CRC32()可以正常工作   好(如果不能,则可以使用64位哈希函数)。建立另一个   柱。 [...] CRC列不保证唯一,因此您   需要在WHERE子句中同时使用这两个条件,否则此技术将无效。   哈希冲突很快发生;你可能会与   大约10万个值,这比您想象的要早得多-不要   假设32位哈希表示您可以将40亿行放入   撞到桌子之前。

答案 6 :(得分:0)

如果您使用二进制(16)数据类型,这可能会很有用:

INSERT INTO table (UUID) VALUES
   (UNHEX(REPLACE(UUID(), "-","")))