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.