如何将UUID存储为数字?

81

根据问题的回答,MySQL中UUID的性能,回答者建议将UUID存储为数字而不是字符串。我不太确定如何做到这一点。有人可以给我提供建议吗?我的Ruby代码该怎么处理?


5
仅当您将UUID用作主键时,性能问题才会出现,因为UUID不是非常有效的主键。您为什么需要UUID?您是否可以保留UUID并将自动增量用作主键? - Thom Smith
4
@ThomSmith 关于“UUID不是很高效的主键”,您能否引用一个解释为什么的来源? - Pacerier
2
这是一块较大的数据,通常需要更多的指令来进行比较。它不是顺序的,所以索引的开销稍微高一些。当然,如果您将其存储为字符串而不是128位数字(似乎OP正在这样做),情况会变得更糟。这不是一个糟糕的密钥,但除非有某些外部原因需要使用它,否则我不会使用它。 - Thom Smith
Percona博客有一篇文章(包括基准测试),回答了您的问题:以优化的方式存储UUID - dolmen
自动递增可能会在多个共享数据库服务器上引起问题,通常会导致键冲突。UUID旨在解决这类问题。如果您将UUID存储为bin(16)而不是文本,则当然具有数字UUID。比较二进制比文本更快。这里有一个讨论此问题的网站 - http://mysql.rjweb.org/doc.php/uuid - Jeff Clayton
2个回答

116
如果我理解正确,您在主键列中使用UUID?人们会说,使用常规(整数)主键会更快,但还有另一种方法是使用MySQL的黑暗面。实际上,当需要索引时,MySQL使用二进制比其他任何东西都要快。
由于UUID为128位并且以十六进制编写,因此很容易加速并存储UUID。
首先,在您的编程语言中删除破折号。
110E8400-E29B-11D4-A716-446655440000变为110E8400E29B11D4A716446655440000
现在它是32个字符长(像MD5哈希一样,这也适用于它)。
由于MySQL中的单个BINARY大小为8位,因此BINARY(16)是UUID的大小(8*16 = 128)。
您可以使用以下方式插入: INSERT INTO Table (FieldBin) VALUES (UNHEX("110E8400E29B11D4A716446655440000")) 并使用以下方式查询: SELECT HEX(FieldBin) AS FieldBin FROM Table 现在在您的编程语言中,在第9、14、19和24个位置重新插入破折号,以匹配原始的UUID。如果位置总是不同,则可以将该信息存储在第二个字段中。
完整示例:
CREATE TABLE  `test_table` (
    `field_binary` BINARY( 16 ) NULL ,
    PRIMARY KEY (  `field_binary` )
) ENGINE = INNODB ;

INSERT INTO  `test_table` (
    `field_binary`
)
VALUES (
    UNHEX(  '110E8400E29B11D4A716446655440000' )
);

SELECT HEX(field_binary) AS field_binary FROM `test_table`

如果你想在任何十六进制字符串中使用这种技术,总是要对字段长度进行 length / 2。所以对于一个 sha512,字段将是 BINARY (64),因为 sha512 编码的长度为 128 个字符。


3
假设你的数据库中有10,000行数据,它们使用UNHEX函数添加,并且你想搜索UUID 110E8400-E29B-11D4-A716-446655440000。只需像这样执行:SELECT * FROM test_table WHERE field_binary LIKE CONCAT("%", UNHEX('110E8400E29B11D4A716446655440000'), "%") - David Bélanger
5
如果你有时间,你可以阅读这篇文章。关注第三点:http://www.xaprb.com/blog/2009/02/12/5-ways-to-make-hexadecimal-identifiers-perform-better-on-mysql/。该文章介绍了5种方法来提高MySQL中十六进制标识符的性能。 - David Bélanger
4
@Chamnap 是的,你可以这样做,你应该这样做。我只是想演示如何在LIKE语句中使用UNHEX函数来使用百分号(%)这个字符。你可以这样写:WHERE Field = UNHEX('110E8400E29B11D4A716446655440000')。而不是写 WHERE Field = 3 或其他任何内容,当你使用十六进制字符串进行搜索、插入、更新、删除等操作时,需要用UNHEX将字段包装起来;当你想从MySQL中读取数据(使用SELECT)时,则需要用HEX将字段包装起来。 - David Bélanger
2
@DavidBélanger 你说MySQL对二进制索引比整数更快。有任何来源吗? - Pacerier
4
在MySQL中,"BINARY"类型的措辞有些令人困惑。一个单独的"BINARY"是8位大小,这就是为什么BINARY(16)起作用(8 * 16 = 128,即UUID的大小)。它并不是“以1位存储16进制所需的4位”。这是不可能的。每个8位大小的BINARY单位可以存储两个十六进制值,因此我们需要16个BINARY单位大小,因此我们使用BINARY(16)。 - lilbyrdie
显示剩余8条评论

1
我认为使用二进制并不是一个好主意。
假设你想查询某个值:
SELECT HEX(field_binary) AS field_binary FROM `test_table`

如果我们需要返回多个值,那么就需要多次调用HEX函数。
然而,主要问题在于:
SELECT * FROM `test_table`
    where field_binary=UNHEX('110E8400E29B11D4A716446655440000')

使用where内的函数会简单地忽略索引。
另外。
SELECT * FROM `test_table`
    where field_binary=x'skdsdfk5rtirfdcv@#*#(&#@$9' 

可能导致许多问题。


4
你测试了你所担心的性能吗?你在暗示 HEX 和 UNHEX 的性能比使用 36 字符字段作为索引的性能问题更严重。我甚至不需要测试就知道这是错误的。(但既然你认为有问题,那就进行测试吧)。其次,你展示的代码不是最佳处理方式。所有数据库代码都应该只涉及到这个 16 字节的字段。不要使用 Hex 和 Unhex。只需将它传递给数据库并从数据库中获取这 16 个字节即可。 使用这些 16 字节值直接进行所有查询。仅在向用户显示时,您需要将其转换为用户友好的版本。 - ToolmakerSteve

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接