source

MySQL에서 UUIDv4를 생성하는 방법은 무엇입니까?

factcode 2023. 8. 27. 09:56
반응형

MySQL에서 UUIDv4를 생성하는 방법은 무엇입니까?

MySQLUUIDfunction이 UUIDv1 GUID를 반환합니다.SQL에서 임의의 GUID(예: UUIDv4)를 쉽게 생성할 수 있는 방법을 찾고 있습니다.

저는 솔루션을 찾는 데 상당한 시간을 들였고 표준 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's)RAND)는 암호화적으로 안전하지 않으므로 충돌 위험을 증가시킬 수 있는 일부 편향이 있습니다.

두 가지 기존 답변은 모두 MySQL 함수에 의존합니다.

랜드()는 완벽한 랜덤 생성기가 아닙니다.이는 동일한 MySQL 버전의 플랫폼 간에 이동할 수 있는 온디맨드 랜덤 번호를 빠르게 생성할 수 있는 방법입니다.

실제로, 이것은 생성된 것을 의미합니다.UUID이 함수를 사용하면 편향될 수 있으며(그리고 그럴 것이며) 충돌이 예상보다 더 자주 발생할 수 있습니다.

해결책

다음 함수를 사용하여 MySQL 측에서 안전한 UUID V4를 생성할 수 있습니다.

이 함수는 SSL 라이브러리의 난수 생성기를 사용하여 생성된 렌 랜덤 바이트의 이진 문자열을 반환합니다.

따라서 기능을 다음으로 업데이트할 수 있습니다.

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/ #기타 참조)

시험

다음과 같은 테스트 시나리오를 만들었습니다.40.000.000개의 행이 생성될 때까지 임의 UUID v4를 테이블의 기본 키로 삽입합니다.충돌이 발견되면 행이 증분하여 업데이트됩니다.collisions열:

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

각 기능이 있는 4천만 행 이후의 충돌 합계는 다음과 같습니다.

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

두 시나리오 모두 행 수가 증가함에 따라 충돌 횟수가 증가하는 경향이 있습니다.

DB로 작업 중이고 함수를 생성할 Perm이 없을 경우 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')));

다음을 이용한 엘리아스 소아레스의 답변 적용RANDOM_BYTESDB 함수를 생성하지 않고 다음을 수행합니다.

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))
))

언급URL : https://stackoverflow.com/questions/32965743/how-to-generate-a-uuidv4-in-mysql

반응형