Qu’est-ce qu’un UUID et à quoi ça sert ? 23


Je vous avais déjà parlé des UUID, mais je crois qu’ils méritent un article à eux tout seul.

Les UUID (Universally Unique IDentifiers) sont des séries de lettres et chiffres générées par la machine de telle sorte qu’ils soient garantis d’être uniques, quel que soit le nombre d’appel que vous faites, et ce, n’importe où dans le monde.

Ça ressemble à un truc comme ça :

550e8400-e29b-41d4-a716-446655440000

Si vous lancez la création de millions d’UUID ici, et autant au Japon, en Nouvelle Zélande et au Vénézuéla, vous avez la garantie de ne jamais avoir le même nombre qui sort 2 fois. C’est une garantie statistique: il y a 4×10³⁷ combinaisons possibles pour les versions des algos récents, et il faudrait créer un milliard de UUID par seconde pendant 100 ans pour que le prochain ait 50% de chance d’être un doublon.

Les UUID les plus célèbres sont les GUID (Globally Unique IDentifiers) de Microsoft utilisés pour l’API COM, mais il existe de nombreuses versions. Par version, je veux dire qu’il existe plusieurs algos qui les produisent, basés soit sur l’adresse MAC (cette technique est dépreciée), soit sur des hash (md5, sha1, etc), soit sur une génération peudo aléatoire.

La version 4 du standard ISO/IEC 11578:1996 est justement basée sur du pseudo aléatoire, et est très largement répandue.

Comment ça s’utilise en Python

Il y a un module pour ça ©

from uuid import uuid4
 
uid = uuid4() # créer un objet uuid
 
print type(uid)
## <class 'uuid.UUID'>
 
uid 
## UUID('786d1b69-a603-4eb8-9178-fed2a195a1ed')
 
str(uid) # récupérer la chaîne de caratères
## '786d1b69-a603-4eb8-9178-fed2a195a1ed'
 
print uid.bytes # la même chose en bytes
## apparait ici un merdier de bytes
## mais censuré car ça fait planter wordpress
 
print uid.get_version()
## 4
 
print uid.int # pratique pour stocker comme un nombre
## 160073875847165073709894672235460141549

C’est très basique et facile à utiliser. Le module uuid contient de quoi générer des uuid version 1, 3, 4 et 5. Pour vérifier que deux UUIDs sont égaux, on peut les comparer, où comparer les chaînes.

print uuid4() == uuid4()
## False
 
uid = uuid4()
 
print uid == uid
## True
 
print uid
## 837806a7-6c37-4630-9f6c-9aa7ad0129ed
 
print str(uid) == "837806a7-6c37-4630-9f6c-9aa7ad0129ed"
## True

On peut aussi générer tout ça manuellement :

from uuid import UUID
 
UUID(int=160073875847165073709894672235460141549, version=4)
## UUID('786d1b69-a603-4eb8-9178-fed2a195a1ed')

Usage ?

C’est pratique pour générer des noms uniques.

Les systèmes de fichiers les utilisent pour identifier les disques durs. Les créateurs d’appareils électroniques pour identifier un appareil en particulier, ou une licence.

Les bases de données peuvent les utiliser à la place des entiers autoincrémentés pour les clés primaires. Très utile car l’ID est garanti d’être unique même si on le génère sur pleins de serveurs différents. On peut ainsi pratiquer le sharding, c’est à dire avoir une table (par exemple une table Utilisateurs), mais répartie sur plein de serveurs. Les JOINS sont plus lents par contre.

Nous on l’utilise beaucoup pour les images et les vidéos: c’est à la fois le nom de fichier, et une partie du nom de chaque dossier qui contient le fichier, afin de ne pas avoir trop de fichiers dans un dossier (ce qui peut faire mourir certaines commandes ou outils).


Télécharger le code de l’article.

23 thoughts on “Qu’est-ce qu’un UUID et à quoi ça sert ?

  • François

    Super bibliothèque que j’utilise aussi pour le même usage.
    Par contre, je ne suis pas certain de saisir :

    c’est à la fois le nom de fichier, et une partie du nom de chaque dossier qui contient le fichier,

    Vous tronquez un UUID pour le dossier ? Chaque dossier ne contient qu’un fichier ?
    En tronquant l’UUID, ca réduit l’effet statistique, donc on s’expose à un risque de collision plus fort.

  • Sam Post author

    Le nom de fichier contient le uuid complet, mais il est dans une aboresence de dossiers construites à base de parties du UUIDs. Par exemple:

    >>> from uuid import uuid4
    >>> uid = str(uuid4())
    >>> '/'.join(uid.split('-')[1:4] + [uid]) + '.jpg'
    '77ff/4bda/aa76/f9f6fee2-77ff-4bda-aa76-3bccb6946b08.jpg'

    Du coup quand on a des Tera d’images et de videos, ça évite d’avoir trop de fichiers par dossiers. Ca permet aussi de migrer plus facilement une partie du contenu.

  • François

    Apple utilise une stratégie similaire pour stocker les fichiers CardDAV / CalDAV. Je n’avais jamais compris pourquoi. Merci ! ^_^°

  • Lujeni

    Dans les cas d’usages, il y a aussi pour les Cookies souvent de type ThirdParty ( tout ce qui concerne l’analytique, le reciblage marketing … ). Ca apporte une garantie de l’unicité malgré que ca reste un cookie :)

  • sil

    En général, les managers de la business school n’aiment pas quand les numéros ne se suivent pas dans les tables. Utiliser les uuid a la place de l’auto-increment peut être une solution.

  • Glandos

    Bah alors ?
    Y a un problème dans le RSS généré, à cause de ça :

    print uid.bytes # la même chose en bytes
    ## xmi\xa6N\xb8\x91x\xfeҡ\x95\xa1\xed

    Ça met des caractères invalides sur toutes la ligne :) Et ça plaît pas du tout à mon lecteur de RSS, qui m’empêche de lire vot’blog du coup !

  • Sam Post author

    @glandos: ça sent la gestion merdique de l’encodage par wordpress. Encore un des millions de bugs de ce programme fantastique.

  • Glandos

    Ouais, en attendant, je suis obligé d’attendre que l’article soit suffisamment vieux pour voir les nouveaux articles. Donc va falloir se mettre au boulot ;)

  • kontre

    C’est à cause de WordPress ou de ton lecteur de RSS que ça ne marche pas ? Ça passe bien avec Google Reader (note que je ne t’encourage pas à l’utiliser, mais ça prouve qu’il est possible de ne pas avoir le problème)

  • Sam Post author

    J’ai été mauvaise langue sur WP, chez moi ça passe très bien sur liferea. Donc je suppose que le lecteur de @glandos gère l’encoding à la tronçonneuse.

  • Sam Post author

    Je note le premier commentaire constructif d’@Anucunnilinguiste depuis qu’il a rejoint notre blog. Mais ça valait le coup d’attendre, c’est une très bonne lecture. Ca mérite un tampon tient.

  • Anucunnilinguiste

    @Sam : Merci, mais ça dépend fortement de mon humeur ;)

    Aujourd’hui, il faisait beau. Enfin, je ne voudrais pas que l’on subodore que je ne sois seulement un trou du cul, un trouble-fête, un agitateur voire pire un salafiste, “j’ai beaucoup changé depuis le massacre de la rave de Villemont” ;) *

    *http://www.allocine.fr/film/fichefilm_gen_cfilm=15558.html

  • Glandos

    @Sam: Merci pour la correction !
    Je voulais renvoyer une commande style wget http://sametmax.com/feed/ -O - -o /dev/null | xmllint --valid --noout - mais maintenant que le flux a changé…
    Je précise qu’avant de râler, j’ai téléchargé le RSS avec wget, et j’ai fait une validation avec Netbeans (c’était le seul outil que j’avais sous la main à ce moment). Et ça avait également échoué.
    Google Reader et Liferea sont donc sûrement de meilleurs lecteurs, plus tolérants sur les erreurs…

  • Sam Post author

    J’ai pourtant éditer les bytes que je pensaient responsable de l’echec critique, mais bon, visiblement ça marche pas mieux. Si quelqu’un trouve une solution simple, qu’il fasse tourner.

  • le hollandais volant

    Mh… Mais pourquoi ne pas utiliser quelque chose comme md5(rand()) ? En choisissant une fonction rand() aussi aléatoire que celui pour UUID.

    Le UUID est bien joli avec ses tirets dedans, mais y’a une raison à leur présence dedans ?

  • Sam Post author

    Les tirets sont seulement pour faciliter la lecture/dictée/comparaison visuelle (et identification de version) de l’UUID. Quand on stocke l’uuid, c’est plutôt sous forme de raw bytes ou de int.

    Raisons de ne pas choisir md5(rand()) :

    – c’est un standard. Quand on parle d’uuid v4, c’est la même chose quel que soit le langage (format, longueur de l’uuid, taux de collision, etc)
    – c’est explicite (quand on voit uuid4(), on sait ce qu’on va faire avec, alors qu’un md5() peut servir à plein de choses)
    – on a detecté des collisions dans md5, certes minimes, mais suffisantes pour emmerder quelqu’un qui gère des bases de données gigantesques avec des milliards d’insertion secondes (ex: google qui utilise les uuid pour ses clés dans big table)
    – ça évite de faire des conneries car si on s’y connait pas, choisir la bonne fonction pour rand() c’est risquer de se planter. La crypto et la génération peusdo aléatoire, c’est compliqué.

  • Sam Post author

    Les usages sont différents.

    uuid5 génère un uuid à partir d’une donnée. C’est juste un hash sha1 qui prend en compte un name space. Lui passer deux fois la donnée donne deux fois le même résultat:

    >>> uuid5(uuid.NAMESPACE_URL, 'http://sametmax.com')
    UUID('4c29cbcb-4c6f-5195-b0a8-1636fa52c4a0')
    >>> uuid5(uuid.NAMESPACE_URL, 'http://sametmax.com')
    UUID('4c29cbcb-4c6f-5195-b0a8-1636fa52c4a0')

    – uuid4() est purement aléatoire. Chaque appel donnera quelque chose de différent.

  • Krysztof von Murphy

    Dans les clés primaires de table, l’UUID ça marche, oui, mais c’est un cauchemar pour déboguer. Si je cherche la PK 1234 dans plusieurs tables ou que je crée des requêtes avec ça, je m’en sors. Mais avec des UUID qui tiennent à peine dans les colonnes sur l’écran ? Je pleure…
    (Cas réel : le CRM de SAP) (on va dire que c’est un progrès par rapport aux tables sans clés primaire technique de R/3, même éditeur…)
    Bref, faut vraiment en avoir besoin.

Leave a comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.