如何在MySQL中生成UUIDv4?

问题描述 投票:0回答:4

MySQL 的

UUID
函数返回一个 UUIDv1 GUID。我正在寻找一种在 SQL 中生成随机 GUID(即 UUIDv4)的简单方法。

mysql uuid
4个回答
76
投票

我花了相当长的时间寻找解决方案,并提出了以下解决方案 使用标准 MySQL 生成随机 UUID(即 UUIDv4)的 mysql 函数 功能。我正在回答我自己的问题来分享,希望它会 有用。

-- Change delimiter so that the function body doesn't end the function declaration
DELIMITER //

CREATE FUNCTION uuid_v4()
    RETURNS CHAR(36) NO SQL
BEGIN
    -- Generate 8 2-byte strings that we will combine into a UUIDv4
    SET @h1 = LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0');
    SET @h2 = LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0');
    SET @h3 = LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0');
    SET @h6 = LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0');
    SET @h7 = LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0');
    SET @h8 = LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0');

    -- 4th section will start with a 4 indicating the version
    SET @h4 = CONCAT('4', LPAD(HEX(FLOOR(RAND() * 0x0fff)), 3, '0'));

    -- 5th section first half-byte can only be 8, 9 A or B
    SET @h5 = CONCAT(HEX(FLOOR(RAND() * 4 + 8)),
                LPAD(HEX(FLOOR(RAND() * 0x0fff)), 3, '0'));

    -- Build the complete UUID
    RETURN LOWER(CONCAT(
        @h1, @h2, '-', @h3, '-', @h4, '-', @h5, '-', @h6, @h7, @h8
    ));
END
//
-- Switch back the delimiter
DELIMITER ;

注意:使用的伪随机数生成(MySQL 的

RAND
)不是 加密安全,因此存在一些可能增加冲突的偏差 风险。


37
投票

现有的两个答案都依赖于 MySQL

RAND()
功能:

RAND() 并不意味着是一个完美的随机生成器。这是一种按需生成随机数的快速方法,可在相同 MySQL 版本的平台之间移植。

实际上,这意味着使用此函数生成的

UUID
可能(并且将会)有偏差,并且碰撞可能比预期更频繁地发生。

解决方案

可以使用

random_bytes()
函数在 MySQL 端生成安全的 UUID V4:

此函数返回使用 SSL 库的随机数生成器生成的 len 随机字节的二进制字符串。

因此我们可以将函数更新为:

CREATE FUNCTION uuid_v4s()
    RETURNS CHAR(36)
BEGIN
    -- 1th and 2nd block are made of 6 random bytes
    SET @h1 = HEX(RANDOM_BYTES(4));
    SET @h2 = HEX(RANDOM_BYTES(2));

    -- 3th block will start with a 4 indicating the version, remaining is random
    SET @h3 = SUBSTR(HEX(RANDOM_BYTES(2)), 2, 3);

    -- 4th block first nibble can only be 8, 9 A or B, remaining is random
    SET @h4 = CONCAT(HEX(FLOOR(ASCII(RANDOM_BYTES(1)) / 64)+8),
                SUBSTR(HEX(RANDOM_BYTES(2)), 2, 3));

    -- 5th block is made of 6 random bytes
    SET @h5 = HEX(RANDOM_BYTES(6));

    -- Build the complete UUID
    RETURN LOWER(CONCAT(
        @h1, '-', @h2, '-4', @h3, '-', @h4, '-', @h5
    ));
END

这应该生成足够随机的 UUID V4,无需关心冲突。

注意:不幸的是,MariaDB不支持

RANDOM_BYTES()
(参见https://mariadb.com/kb/en/function-differences- Between-mariadb-105-and-mysql-80/#miscellaneous

测试

我创建了以下测试场景:插入随机 UUID v4 作为表的主键,直到创建 40.000.000 行。发现碰撞时,该行将更新,递增

collisions
列:

INSERT INTO test (uuid) VALUES (uuid_v4()) ON DUPLICATE KEY UPDATE collisions=collisions+1;

每个函数 4000 万行后的碰撞总和为:

+----------+----------------+
| RAND()   | RANDOM_BYTES() |
+----------+----------------+
|       55 |              0 |
+----------+----------------+

随着行数的增加,这两种情况下的冲突数量都会增加。


29
投票

使用

RANDOM_BYTES
改编 Elias Soares 的答案,而不创建数据库函数:

SELECT LOWER(CONCAT(
    HEX(RANDOM_BYTES(4)), '-',
    HEX(RANDOM_BYTES(2)), '-4',
    SUBSTR(HEX(RANDOM_BYTES(2)), 2, 3), '-',
    CONCAT(HEX(FLOOR(ASCII(RANDOM_BYTES(1)) / 64)+8),SUBSTR(HEX(RANDOM_BYTES(2)), 2, 3)), '-',
    HEX(RANDOM_BYTES(6))
))

27
投票

万一您正在使用数据库并且没有创建函数的权限,这里有与上面相同的版本,它的作用就像 SQL 表达式一样:

SELECT LOWER(CONCAT(
    LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'), 
    LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'), '-',
    LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'), '-', 
    '4',
    LPAD(HEX(FLOOR(RAND() * 0x0fff)), 3, '0'), '-', 
    HEX(FLOOR(RAND() * 4 + 8)), 
    LPAD(HEX(FLOOR(RAND() * 0x0fff)), 3, '0'), '-', 
    LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'),
    LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'),
    LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0')));
© www.soinside.com 2019 - 2024. All rights reserved.