web-dev-qa-db-fra.com

Comment devrais-je stocker GUID dans les tables MySQL?

Est-ce que j'utilise varchar (36) ou existe-t-il de meilleures façons de le faire?

136
CDR

Mon administrateur de base de données m'a demandé quand je demandais quel était le meilleur moyen de stocker des GUID pour mes objets et pourquoi je devais stocker 16 octets alors que je pouvais faire la même chose sur 4 octets avec un Integer. Depuis qu'il m'a lancé ce défi, je pensais que le moment était bien choisi pour le mentionner. Cela étant dit...

Vous pouvez stocker un GUID sous la forme d'un binaire CHAR (16) si vous souhaitez optimiser l'utilisation de l'espace de stockage.

97
thaBadDawg

Je voudrais le stocker comme un caractère (36).

41
Brian Fisher

Ajoutant à la réponse de ThaBadDawg, utilisez ces fonctions pratiques (grâce à un de mes collègues plus sages) pour passer de 36 chaînes de longueur à un tableau de 16 octets.

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, prenons l'exemple donné ci-dessous, ordonné par chiffres GUID. (Les caractères illégaux sont utilisés à des fins d'illustration - chaque place est un caractère unique.) obtenir un ordre de bits pour une mise en cluster d'index supérieure.Le guide réordonné est présenté sous l'exemple.

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

Tirets supprimés:

123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW
31
KCD

car (36) serait un bon choix. De plus, la fonction UUID () de MySQL peut être utilisée pour renvoyer un format de texte de 36 caractères (hex avec traits d'union) pouvant être utilisé pour récupérer ces identifiants à partir de la base de données.

24
Learning

"Mieux" dépend de ce que vous optimisez.

À quel point la taille/les performances de stockage vous importent-elles par rapport à la facilité de développement? Plus important encore - générez-vous suffisamment de GUID ou récupérez-vous-les assez souvent pour que cela compte?

Si la réponse est "non", char(36) suffit amplement, et il est extrêmement simple de stocker/récupérer des GUID. Sinon, binary(16) est raisonnable, mais vous devrez vous appuyer sur MySQL et/ou le langage de programmation de votre choix pour effectuer la conversion à partir de la représentation sous forme de chaîne habituelle.

17
candu

Binaire (16) serait bien, mieux que l’utilisation de varchar (32).

8
Onkar Janwa

La routine GuidToBinary publiée par KCD doit être modifiée pour tenir compte de la structure de bits de l'horodatage dans la chaîne GUID). Si la chaîne représente un UUID de version 1, comme ceux renvoyés par uuid () mysql routine, 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 pour l'indexation serait: EFG9ABC12345678D + le reste.

Vous ne voulez pas échanger de 12345678 à 78563412 car big endian donne déjà le meilleur ordre d'octet d'indice binaire. Cependant, vous voulez que les octets les plus significatifs soient déplacés devant les octets inférieurs. Par conséquent, EFG commence en premier, suivi des bits du milieu et des bits inférieurs. Générez une douzaine d'UUID avec uuid () en l'espace d'une minute et vous devriez voir comment cet ordre génère le bon classement.

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 le plus rapidement possible. Ils ne varient que dans les 3 derniers petits du premier bloc. Ce sont les bits les moins significatifs de l'horodatage, ce qui signifie que nous souhaitons les pousser à droite lorsque nous convertissons cela en un tableau d'octets indexables. Comme exemple de compteur, le dernier identifiant est le plus récent, mais l'algorithme de permutation de KCD le mettrait avant le 3ème identifiant (3e avant ms, derniers octets du premier bloc).

L'ordre correct pour l'indexation serait:

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

Voir cet article pour plus d'informations: http://mysql.rjweb.org/doc.php/uuid

*** Notez que je ne sépare pas le nibble de version des 12 bits de l'horodatage. Ceci est le D nibble de votre exemple. Je viens de le jeter 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 nibble. L'article fait la même chose.

7
bigh_29

Pour ceux qui viennent de tomber sur cela, il existe maintenant une alternative bien meilleure d'après les recherches de Percona.

Cela consiste à réorganiser les fragments UUID pour une indexation optimale, puis à les convertir en binaire pour un stockage réduit.

Lire l'article complet ici

5
sleepycal

Je suggérerais d'utiliser les fonctions ci-dessous car celles mentionnées par @ bigh_29 transforment mes guids en nouveaux (pour des raisons que je ne comprends pas). En outre, 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 ;
2
vsdev