我根据找到的函数here
使用PHP生成UUID现在我想将它存储在MySQL数据库中。存储UUID v4的最佳/最有效的MySQL字段格式是什么?
我目前有varchar(256),但我非常确定它比必要的要大得多。我发现了很多差不多的答案,但他们对于他们指的是什么形式的UUID一般都很模糊,所以我要求提供具体的格式。
答案 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(), "-","")))