Pregunta

¿Uso varchar (36) o hay mejores maneras de hacerlo?

¿Fue útil?

Solución

Mi DBA me preguntó cuando pregunté cuál era la mejor forma 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 una guía como un binario CHAR (16) si desea aprovechar al máximo el espacio de almacenamiento.

Otros consejos

Lo almacenaría como un char (36).

Para agregar a la respuesta de ThaBadDawg, use estas funciones prácticas (gracias a un colega más sabio) para volver de una cadena de 36 longitud 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) , elige tu 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 uno coloca un carácter único). Las funciones transformarán el orden de los bytes para lograr un orden de bits para una agrupación de índices superior. El guid reordenado se muestra debajo del ejemplo.

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

Se eliminaron los guiones:

123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW

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 ID de la base de datos.

" Mejor " Depende de para qué estés optimizando.

¿Cuánto le importa el tamaño / rendimiento del almacenamiento en comparación con la facilidad de desarrollo? Y lo que es más importante, ¿está generando suficientes GUID o los está buscando con la frecuencia suficiente para que sea importante?

Si la respuesta es " no " ;, char (36) es más que suficiente, y hace que el almacenamiento / captura de GUID sea extremadamente simple. De lo contrario, binary (16) es razonable, pero tendrá que apoyarse en MySQL y / o en el lenguaje de programación que elija para realizar conversiones de ida y vuelta desde la representación de cadena habitual.

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

La rutina GuidToBinary publicada por KCD debe ajustarse para tener en cuenta el diseño de bits de la marca de tiempo en la cadena GUID. Si la cadena representa un UUID de la versión 1, como los devueltos por la rutina uuid () mysql, los componentes de tiempo están incrustados en las 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

Al convertir a binario, el mejor orden para la indexación sería: EFG9ABC12345678D + el resto.

No desea intercambiar 12345678 a 78563412 porque Big Endian ya produce el mejor orden de bytes de índice binario. Sin embargo, desea que los bytes más significativos se muevan frente a los bytes inferiores. Por lo tanto, EFG va primero, seguido por los bits medios y los bits inferiores. Genera una docena de UUID con uuid () en el transcurso de un minuto y deberías 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 cercanos 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, lo que significa que queremos empujarlos a la derecha cuando los convertimos en una matriz de bytes indexables. Como ejemplo de contador, el último ID es el más actual, pero el algoritmo de intercambio del 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 los 12 bits más altos de la marca de tiempo. Este es el mordisco en D de tu ejemplo. Acabo de tirarlo al frente. Así que mi secuencia binaria termina siendo DEFG9ABC y así sucesivamente. Esto implica que todos mis UUID indexados comienzan con el mismo nibble. El artículo hace lo mismo.

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

Consiste en reorganizar los fragmentos UUID para obtener una indexación óptima y luego convertirlos en binarios para reducir el almacenamiento.

Lea el artículo completo aquí

Sugeriría usar las funciones siguientes, ya que las mencionadas en @ bigh_29 transforman mis guías en nuevas (por razones que no entiendo). Además, estos son un poco más rápidos en las pruebas que hice en mis tablas. 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 ;

si tiene un valor char / varchar formateado como GUID estándar, simplemente puede almacenarlo como BINARY (16) usando el simple CAST (MyString AS BINARY16), sin todas esas secuencias alucinantes de CONCAT + SUBSTR.

Los campos

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top