将UUID v4存储在MySQL中

问题描述 投票:13回答:5

我根据找到的函数here使用PHP生成UUID

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

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

mysql uuid
5个回答
31
投票

将它存储为VARCHAR(36),如果你想要精确配合,或VARCHAR(255),无论如何都会以相同的存储成本。这里没有理由对字节大惊小怪。

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

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

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


11
投票

如果每行总有一个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 SET utf8mb4列中的每个字符保留四个字节,因为这是可能的最大长度。例如,MySQL必须为CHAR(10)CHARACTER SET utf8mb4列保留40个字节。 https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html


10
投票

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

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

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

INSERT IN TO VALUES(UUID_TO_BIN(UUID(),true))


1
投票

最有效的绝对是BINARY(16),存储人类可读的字符使用超过存储空间的两倍,并且意味着更大的索引和更慢的查找。如果您的数据足够小,以便像文本一样存储不会影响性能,那么您可能不需要UUID而不是无聊的整数键。存储raw并不像其他人建议的那样痛苦,因为任何像样的数据库管理工具都会以八进制显示/转储八位字节,而不是“文本”的字面字节。您不应该在数据库中手动查找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;

1
投票

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

前者可能会给您带来麻烦,因为手动查询不会(以简单的方式)为您提供可读/可复制的值。后者可能会让您头疼,因为必须在一个值和两个列之间进行映射。

如果这是一个主键,我肯定不会浪费任何空间,因为它也成为每个二级索引的一部分。换句话说,我会选择其中一种类型。

为了性能,随机UUID(即UUID v4,随机化)的随机性会严重受损。当UUID是您的主键或者您对其执行大量范围查询时,这适用。您在主索引中的插入将遍布整个地方,而不是全部(或接近)结束。您的数据会丢失时间局部性,这在各种情况下都是有用的属性。

我的主要改进是使用类似于UUID v1的东西,它使用时间戳作为其数据的一部分,并确保时间戳位于最高位。例如,UUID可能是这样的:

Timestamp | Machine Identifier | Counter

这样,我们得到一个类似于自动增量值的位置。

© www.soinside.com 2019 - 2024. All rights reserved.