Dois-je utiliser varchar (36) ou y a-t-il de meilleures façons de le faire?
Dois-je utiliser varchar (36) ou y a-t-il de meilleures façons de le faire?
Réponses:
Mon DBA m'a demandé quand j'ai demandé la meilleure façon de stocker les GUID pour mes objets pourquoi je devais stocker 16 octets alors que je pouvais faire la même chose en 4 octets avec un entier. Depuis qu'il m'a lancé ce défi, j'ai pensé que le moment était venu de le mentionner. Cela étant dit...
Vous pouvez stocker un guid sous forme de binaire CHAR (16) si vous souhaitez utiliser au mieux l'espace de stockage.
Je le stockerais sous forme de caractère (36).
-
s.
Pour ajouter à la réponse de ThaBadDawg, utilisez ces fonctions pratiques (grâce à un de mes collègues plus sages) pour passer d'une chaîne de 36 longueurs à un tableau d'octets 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)
est en fait un BINARY(16)
, choisissez votre saveur préférée
Pour mieux suivre le code, prenez l'exemple donné le GUID par ordre de chiffres ci-dessous. (Les caractères illégaux sont utilisés à des fins d'illustration - chaque place un caractère unique.) Les fonctions transformeront l'ordre des octets pour obtenir un ordre de bits pour une classification d'index supérieure. Le guid réorganisé est affiché sous l'exemple.
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW
Tirets supprimés:
123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW
GuidToBinary
($ guid char (36)) RETURNS binary (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
et une BINARY
équivalence ( la documentation semble impliquer qu'il existe des différences importantes et une explication des raisons pour lesquelles les performances des index clusterisés sont meilleures avec des octets réorganisés.
char (36) serait un bon choix. La fonction UUID () de MySQL peut également être utilisée, ce qui renvoie un format de texte à 36 caractères (hex avec tirets) qui peut être utilisé pour récupérer ces ID à partir de la base de données.
«Mieux» dépend de ce pour quoi vous optimisez.
Dans quelle mesure vous souciez-vous de la taille / des performances du stockage par rapport à la facilité de développement? Plus important encore, générez-vous suffisamment de GUID ou les récupérez-vous assez fréquemment pour que cela compte?
Si la réponse est "non", char(36)
c'est plus que suffisant, et cela rend le stockage / récupération des GUID extrêmement simple. Sinon, binary(16)
c'est raisonnable, mais vous devrez vous appuyer sur MySQL et / ou votre langage de programmation de choix pour convertir dans les deux sens à partir de la représentation sous forme de chaîne habituelle.
Binary (16) serait bien, mieux que l'utilisation de varchar (32).
La routine GuidToBinary publiée par KCD doit être modifiée pour tenir compte de la disposition de bits de l'horodatage dans la chaîne GUID. Si la chaîne représente un UUID version 1, comme ceux retournés par la routine mysql uuid (), alors les composants de temps sont incorporés dans les lettres 1-G, à l'exclusion du 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
Lorsque vous convertissez en binaire, le meilleur ordre d'indexation serait: EFG9ABC12345678D + le reste.
Vous ne voulez pas échanger 12345678 contre 78563412 car big endian produit déjà le meilleur ordre d'octets d'index binaire. Cependant, vous voulez que les octets les plus significatifs soient déplacés devant les octets inférieurs. Par conséquent, EFG passe en premier, suivi des bits du milieu et des bits inférieurs. Générez une douzaine d'UUID avec uuid () en une minute et vous devriez voir comment cet ordre donne le rang correct.
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
Les deux premiers UUID ont été générés au plus près dans le temps. Ils ne varient que dans les 3 derniers grignotages du premier bloc. Ce sont les bits les moins significatifs de l'horodatage, ce qui signifie que nous voulons les pousser vers la droite lorsque nous le convertissons en un tableau d'octets indexables. À titre d'exemple de compteur, le dernier ID est le plus courant, mais l'algorithme de permutation du KCD le placerait avant le 3e ID (3e avant dc, derniers octets du premier bloc).
L'ordre correct pour l'indexation serait:
1e497556eec5eb6...
1e497556eec5f10...
1e497556eec8ddc...
1e497556eee30d0...
1e497556efda038...
1e497556f9641bf...
1e49755758c3e3e...
Consultez cet article pour obtenir des informations complémentaires : http://mysql.rjweb.org/doc.php/uuid
*** notez que je ne sépare pas le grignotage de la version des 12 bits supérieurs de l'horodatage. C'est le grignotage D de votre exemple. Je le jette juste devant. Donc ma séquence binaire finit par être DEFG9ABC et ainsi de suite. Cela implique que tous mes UUID indexés commencent par le même quartet. L'article fait la même chose.
Pour ceux qui ne font que trébucher sur cela, il existe maintenant une bien meilleure alternative selon les recherches de Percona.
Il consiste à réorganiser les blocs UUID pour une indexation optimale, puis à les convertir en binaire pour un stockage réduit.
Lisez l'article complet ici
Je suggérerais d'utiliser les fonctions ci-dessous puisque celles mentionnées par @ bigh_29 transforment mes guides en de nouveaux (pour des raisons que je ne comprends pas). De plus, ceux-ci sont un peu plus rapides dans les tests que j'ai effectués sur mes tables. 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 vous avez une valeur char / varchar formatée comme GUID standard, vous pouvez simplement la stocker en tant que BINARY (16) en utilisant le simple CAST (MyString AS BINARY16), sans toutes ces séquences époustouflantes de CONCAT + SUBSTR.
Les champs BINARY (16) sont comparés / triés / indexés beaucoup plus rapidement que les chaînes, et prennent également deux fois moins d'espace dans la base de données
select CAST("hello world, this is as long as uiid" AS BINARY(16));
produithello world, thi