¿Cómo debo almacenar GUID en tablas MySQL?


¿Utilizo varchar(36) o hay alguna forma mejor de hacerlo?

Author: conny, 2009-01-05

10 answers

Mi DBA me preguntó cuando pregunté sobre la mejor manera de almacenar GUID para mis objetos por qué necesitaba almacenar 16 bytes cuando podía hacer lo mismo en 4 bytes con un entero. Desde que me puso ese desafío pensé que ahora era un buen momento para mencionarlo. Dicho esto...

Puede almacenar un guid como un binario CHAR(16) si desea hacer el uso más óptimo del espacio de almacenamiento.

 92
Author: thaBadDawg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-05 05:44:13

Lo almacenaría como un char(36).

 38
Author: Brian Fisher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-05 05:41:27

Añadiendo a la respuesta de ThaBadDawg, use estas prácticas funciones (gracias a un colega mío más sabio) para volver de una cadena de 36 longitudes a una matriz de bytes de 16.

DELIMITER $$

CREATE FUNCTION `GuidToBinary`(
    $Data VARCHAR(36)
) RETURNS binary(16)
DETERMINISTIC
NO SQL
BEGIN
    DECLARE $Result BINARY(16) DEFAULT NULL;
    IF $Data IS NOT NULL THEN
        SET $Data = REPLACE($Data,'-','');
        SET $Result =
            CONCAT( UNHEX(SUBSTRING($Data,7,2)), UNHEX(SUBSTRING($Data,5,2)),
                    UNHEX(SUBSTRING($Data,3,2)), UNHEX(SUBSTRING($Data,1,2)),
                    UNHEX(SUBSTRING($Data,11,2)),UNHEX(SUBSTRING($Data,9,2)),
                    UNHEX(SUBSTRING($Data,15,2)),UNHEX(SUBSTRING($Data,13,2)),
                    UNHEX(SUBSTRING($Data,17,16)));
    END IF;
    RETURN $Result;
END

$$

CREATE FUNCTION `ToGuid`(
    $Data BINARY(16)
) RETURNS char(36) CHARSET utf8
DETERMINISTIC
NO SQL
BEGIN
    DECLARE $Result CHAR(36) DEFAULT NULL;
    IF $Data IS NOT NULL THEN
        SET $Result =
            CONCAT(
                HEX(SUBSTRING($Data,4,1)), HEX(SUBSTRING($Data,3,1)),
                HEX(SUBSTRING($Data,2,1)), HEX(SUBSTRING($Data,1,1)), '-', 
                HEX(SUBSTRING($Data,6,1)), HEX(SUBSTRING($Data,5,1)), '-',
                HEX(SUBSTRING($Data,8,1)), HEX(SUBSTRING($Data,7,1)), '-',
                HEX(SUBSTRING($Data,9,2)), '-', HEX(SUBSTRING($Data,11,6)));
    END IF;
    RETURN $Result;
END
$$

CHAR(16) es en realidad un BINARY(16), elija su sabor preferido

Para seguir mejor el código, tome el ejemplo dado el GUID ordenado por dígitos a continuación. (Los caracteres ilegales se utilizan con fines ilustrativos: cada lugar es un carácter único.) Las funciones transformarán el orden de bytes para lograr un orden de bits para clustering de índice superior. El guid reordenado se muestra debajo del ejemplo.

12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW

Guiones eliminados:

123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW
 30
Author: KCD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-22 21:30:40

Char(36) sería una buena opción. También se puede usar la función UUID() de MySQL que devuelve un formato de texto de 36 caracteres (hexadecimal con guiones) que se puede usar para recuperar dichos IDs de la base de datos.

 23
Author: Learning,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-05 05:47:58

"Mejor" depende de lo que estés optimizando.

¿Cuánto le importa el tamaño/rendimiento del almacenamiento frente a la facilidad de desarrollo? Más importante aún - ¿estás generando suficientes GUIDs, o buscándolos con la frecuencia suficiente, que importa?

Si la respuesta es "no", char(36) es más que suficiente, y hace que almacenar/obtener GUID sea muy simple. De lo contrario, binary(16) es razonable, pero tendrá que apoyarse en MySQL y / o su lenguaje de programación de su elección para convertir de un lado a otro de la representación de cadena habitual.

 14
Author: candu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-28 16:26:30

Binario(16) estaría bien, mejor que el uso de varchar(32).

 8
Author: Onkar Janwa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-31 12:00:16

La rutina GuidToBinary publicada por KCD debe ser ajustada para tener en cuenta el diseño de bits de la marca de tiempo en la cadena GUID. Si la cadena representa un UUID versión 1, como los devueltos por la rutina mysql uuid (), entonces los componentes de tiempo están incrustados en letras 1-G, excluyendo la D.

12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
12345678 = least significant 4 bytes of the timestamp in big endian order
9ABC     = middle 2 timestamp bytes in big endian
D        = 1 to signify a version 1 UUID
EFG      = most significant 12 bits of the timestamp in big endian

Cuando se convierte a binario, el mejor orden para la indexación sería: EFG9ABC12345678D + el resto.

No quieres cambiar 12345678 a 78563412 porque big endian ya rinde el mejor orden de bytes de índice binario. Sin embargo, desea que los bytes más significativos se muevan delante de los bytes más bajos. Por lo tanto, EFG ir primero, seguido por los bits medios y bits inferiores. Genere una docena de UUID con uuid () en el transcurso de un minuto y debería ver cómo este orden produce el rango correcto.

select uuid(), 0
union 
select uuid(), sleep(.001)
union 
select uuid(), sleep(.010)
union 
select uuid(), sleep(.100)
union 
select uuid(), sleep(1)
union 
select uuid(), sleep(10)
union
select uuid(), 0;

/* output */
6eec5eb6-9755-11e4-b981-feb7b39d48d6
6eec5f10-9755-11e4-b981-feb7b39d48d6
6eec8ddc-9755-11e4-b981-feb7b39d48d6
6eee30d0-9755-11e4-b981-feb7b39d48d6
6efda038-9755-11e4-b981-feb7b39d48d6
6f9641bf-9755-11e4-b981-feb7b39d48d6
758c3e3e-9755-11e4-b981-feb7b39d48d6 

Los dos primeros UUID se generaron más cerca en el tiempo. Solo varían en los últimos 3 mordiscos del primer bloque. Estos son los bits menos significativos de la marca de tiempo, que significa que queremos empujarlos a la derecha cuando convertimos esto en una matriz de bytes indexable. Como ejemplo de contador, el último ID es el más actual, pero el algoritmo de intercambio de KCD lo pondría antes del 3er ID (3e antes de dc, últimos bytes del primer bloque).

El orden correcto para la indexación sería:

1e497556eec5eb6... 
1e497556eec5f10... 
1e497556eec8ddc... 
1e497556eee30d0... 
1e497556efda038... 
1e497556f9641bf... 
1e49755758c3e3e... 

Consulte este artículo para obtener información de apoyo: http://mysql.rjweb.org/doc.php/uuid

* * * tenga en cuenta que no divido la versión nibble de la alta 12 bits de la marca de tiempo. Este es el mordisco D de su ejemplo. Lo tiro al frente. Así que mi secuencia binaria termina siendo DEFG9ABC y así sucesivamente. Esto implica que todos mis UUID indexados comienzan con el mismo mordisco. El artículo hace lo mismo.

 6
Author: bigh_29,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-08 17:19:00

Para aquellos que se encuentran con esto, ahora hay una alternativa mucho mejor según la investigación de Percona.

Consiste en reorganizar los trozos de UUID para una indexación óptima, luego convertirlos en binarios para un almacenamiento reducido.

Lea el artículo completo aquí

 5
Author: sleepycal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-26 14:24:21

Sugeriría usar las siguientes funciones ya que las mencionadas por @bigh_29 transforman mis guid en otros nuevos (por razones que no entiendo). Además, estos son un poco más rápidos en las pruebas que hice en mis mesas. https://gist.github.com/damienb/159151

DELIMITER |

CREATE FUNCTION uuid_from_bin(b BINARY(16))
RETURNS CHAR(36) DETERMINISTIC
BEGIN
  DECLARE hex CHAR(32);
  SET hex = HEX(b);
  RETURN LOWER(CONCAT(LEFT(hex, 8), '-', MID(hex, 9,4), '-', MID(hex, 13,4), '-', MID(hex, 17,4), '-', RIGHT(hex, 12)));
END
|

CREATE FUNCTION uuid_to_bin(s CHAR(36))
RETURNS BINARY(16) DETERMINISTIC
RETURN UNHEX(CONCAT(LEFT(s, 8), MID(s, 10, 4), MID(s, 15, 4), MID(s, 20, 4), RIGHT(s, 12)))
|

DELIMITER ;
 1
Author: vsdev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-17 15:22:12

Si tiene un valor char/varchar formateado como el GUID estándar, simplemente puede almacenarlo como BINARIO(16) utilizando el CAST simple(myString COMO BINARY16), sin todas esas alucinantes secuencias de CONCAT + SUBSTR.

Los campos BINARIOS(16) se comparan/ordenan/indexan mucho más rápido que las cadenas, y también ocupan dos veces menos espacio en la base de datos

 -4
Author: George Hazan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-04 21:13:14