Saltar al contenido

¿Cómo generar un UUIDv4 en MySQL?

Esta división fue analizado por expertos para que tengas la garantía de la exactitud de nuestra esta crónica.

Solución:

Pasé bastante tiempo buscando una solución y se me ocurrió la siguiente función mysql que genera un UUID aleatorio (es decir, UUIDv4) usando funciones estándar de MySQL. Estoy respondiendo mi propia pregunta para compartir eso con la esperanza de que sea útil.

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

Nota: La generación de números pseudoaleatorios utilizada (MySQL’s RAND) no es criptográficamente seguro y, por lo tanto, tiene algún sesgo que puede aumentar el riesgo de colisión.

En el caso de que esté trabajando con una base de datos y no tenga permisos para crear funciones, esta es la misma versión que la anterior que funciona solo como una expresión 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')));

Ambas respuestas existentes se basan en MySQL RAND() función:

RAND() no pretende ser un generador aleatorio perfecto. Es una forma rápida de generar números aleatorios bajo demanda que es portátil entre plataformas para la misma versión de MySQL.

En la práctica, esto significa que los generados UUID el uso de esta función podría (y lo hará) estar sesgado, y las colisiones pueden ocurrir con más frecuencia de lo esperado.

Solución

Es posible generar un UUID V4 seguro en el lado de MySQL usando random_bytes() función:

Esta función devuelve un binario string de len bytes aleatorios generados usando el generador de números aleatorios de la biblioteca SSL.

Entonces podemos actualizar la función a:

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

Esto debería generar UUID V4 lo suficientemente aleatorio como para no preocuparse por las colisiones.

NOTA: Desafortunadamente, MariaDB no es compatible RANDOM_BYTES() (Ver https://mariadb.com/kb/en/function-differences- between-mariadb-105-and-mysql-80/#miscellaneous)

Prueba

Creé el siguiente escenario de prueba: Insertar UUID aleatorio v4 como principal key para una tabla hasta que se creen 40.000.000 filas. Cuando se encuentra una colisión, la fila se actualiza incrementando collisions columna:

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

La suma de colisiones después de 40 millones de filas con cada función es:

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

El número de colisiones en ambos escenarios tiende a aumentar a medida que crece el número de filas.

Reseñas y calificaciones

Si para ti ha sido útil este post, sería de mucha ayuda si lo compartieras con más programadores así nos ayudas a dar difusión a nuestro contenido.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags : / /

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *