Eu uso o varchar (36) ou existem maneiras melhores de fazer isso?
Eu uso o varchar (36) ou existem maneiras melhores de fazer isso?
Respostas:
Meu DBA me perguntou quando perguntei sobre a melhor maneira de armazenar GUIDs para meus objetos por que eu precisava armazenar 16 bytes quando eu poderia fazer a mesma coisa em 4 bytes com um Número Inteiro. Desde que ele colocou esse desafio para mim, pensei que agora era um bom momento para mencioná-lo. Dito isto ...
Você pode armazenar um guia como um binário CHAR (16) se desejar fazer o melhor uso possível do espaço de armazenamento.
Eu o armazenaria como um caractere (36).
-
s.
Acrescentando à resposta de ThaBadDawg, use essas funções úteis (graças a um colega mais sábio) para obter uma sequência de 36 caracteres de comprimento e uma matriz de 16 bytes.
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)
é realmente um BINARY(16)
, escolha seu sabor preferido
Para seguir melhor o código, use o exemplo, conforme o GUID ordenado por dígitos abaixo. (Caracteres ilegais são usados para fins ilustrativos - cada um possui um caractere exclusivo.) As funções transformarão a ordem dos bytes para obter uma ordem de bits para um cluster de índice superior. O guia reordenado é mostrado abaixo do exemplo.
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW
Traços removidos:
123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW
GuidToBinary
($ guid char (36)) RETORNA binário (16) RETURN CONCAT (UNHEX (SUBSTRING ($ guid, 7, 2)), UNHEX (SUBSTRING ($ guid, 5, 2)), UNHEX (SUBSTRING ($ guid, 3, 2)), UNHEX (SUBSTRING ($ guid, 1, 2)), UNHEX (SUBSTRING ($ guid, 12, 2)), UNHEX (SUBSTRING ($ guid, 10, 2)), UNHEX (SUBSTRING ($ guid, 17, 2)), UNHEX (SUBSTRING ($ guid, 15, 2)), UNHEX (SUBSTRING ($ guid, 20, 4)), UNHEX (SUBSTRING ($ guid, 25, 12)));
char (36) seria uma boa escolha. Também é possível usar a função UUID () do MySQL, que retorna um formato de texto de 36 caracteres (hexadecimal com hífens) que pode ser usado para recuperar esses IDs do banco de dados.
"Melhor" depende do que você está otimizando.
Quanto você se importa com tamanho / desempenho de armazenamento versus facilidade de desenvolvimento? Mais importante: você está gerando GUIDs suficientes ou buscando-os com frequência suficiente para que isso importe?
Se a resposta for "não", char(36)
é mais do que suficiente e torna os GUIDs de armazenamento / busca simples. Caso contrário, binary(16)
é razoável, mas você terá que se apoiar no MySQL e / ou na sua linguagem de programação preferida para converter a partir da representação usual de strings.
Binário (16) seria bom, melhor do que o uso de varchar (32).
A rotina GuidToBinary postada pelo KCD deve ser ajustada para levar em conta o layout de bit do registro de data e hora na string GUID. Se a string representa um UUID da versão 1, como aqueles retornados pela rotina mysql uuid (), os componentes de tempo são incorporados nas letras 1-G, excluindo o 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
Ao converter para binário, a melhor ordem para indexação seria: EFG9ABC12345678D + o restante.
Você não deseja trocar 12345678 por 78563412 porque o big endian já produz a melhor ordem de bytes de índice binário. No entanto, você deseja que os bytes mais significativos sejam movidos na frente dos bytes inferiores. Portanto, o EFG vai primeiro, seguido pelos bits do meio e pelos inferiores. Gere uma dúzia de UUIDs com uuid () ao longo de um minuto e você deverá ver como essa ordem gera a classificação correta.
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
Os dois primeiros UUIDs foram gerados mais próximos no tempo. Eles variam apenas nos últimos 3 petiscos do primeiro bloco. Esses são os bits menos significativos do registro de data e hora, o que significa que queremos empurrá-los para a direita quando convertemos isso em uma matriz de bytes indexáveis. Como um exemplo contrário, o último ID é o mais atual, mas o algoritmo de troca do KCD o colocaria antes do 3º ID (3e antes de dc, últimos bytes do primeiro bloco).
A ordem correta para a indexação seria:
1e497556eec5eb6...
1e497556eec5f10...
1e497556eec8ddc...
1e497556eee30d0...
1e497556efda038...
1e497556f9641bf...
1e49755758c3e3e...
Consulte este artigo para obter informações de suporte: http://mysql.rjweb.org/doc.php/uuid
*** observe que não divido a mordidela da versão dos 12 bits mais altos do carimbo de data e hora. Esta é a mordidela D do seu exemplo. Eu apenas jogo na frente. Portanto, minha sequência binária acaba sendo DEFG9ABC e assim por diante. Isso implica que todos os meus UUIDs indexados começam com a mesma mordidela. O artigo faz a mesma coisa.
Para aqueles que apenas tropeçam nisso, agora existe uma alternativa muito melhor conforme a pesquisa da Percona.
Consiste em reorganizar os chunks UUID para obter uma indexação ideal e depois converter em binário para reduzir o armazenamento.
Leia o artigo completo aqui
Eu sugeriria o uso das funções abaixo, pois as mencionadas por @ bigh_29 transformam meus guias em novos (por razões que não entendo). Além disso, estes são um pouco mais rápidos nos testes que fiz nas minhas 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 ;
se você tiver um valor char / varchar formatado como o GUID padrão, poderá simplesmente armazená-lo como BINARY (16) usando o CAST simples (MyString AS BINARY16), sem todas essas sequências impressionantes de CONCAT + SUBSTR.
Os campos BINARY (16) são comparados / classificados / indexados muito mais rapidamente que as strings e também ocupam duas vezes menos espaço no banco de dados
select CAST("hello world, this is as long as uiid" AS BINARY(16));
produzhello world, thi