Sam & Max » uuid http://sametmax.com Du code, du cul Sat, 07 Nov 2015 10:56:13 +0000 en-US hourly 1 http://wordpress.org/?v=4.1 Qu’est-ce qu’un UUID et à quoi ça sert ? 23 http://sametmax.com/quest-ce-quun-uuid-et-a-quoi-ca-sert/ http://sametmax.com/quest-ce-quun-uuid-et-a-quoi-ca-sert/#comments Mon, 04 Mar 2013 18:31:25 +0000 http://sametmax.com/?p=5179 déjà parlé des UUID, mais je crois qu'ils méritent un article à eux tout seul. ]]> 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.

]]>
http://sametmax.com/quest-ce-quun-uuid-et-a-quoi-ca-sert/feed/ 23
Utiliser des UUID comme primary key avec l’ORM de Django 5 http://sametmax.com/utiliser-des-uuid-comme-primary-key-avec-lorm-de-django/ http://sametmax.com/utiliser-des-uuid-comme-primary-key-avec-lorm-de-django/#comments Thu, 06 Dec 2012 14:59:29 +0000 http://sametmax.com/?p=3505 id à tous les modèles, et le configure pour être un entier qui s'auto incrémente puis le désigne comme la clé primaire. Il est néanmoins possible d'utiliser un autre champ comme clé primaire pour sa table: un slug ou un identifiant métier. Dans notre cas, on va voir comme utiliser un UUID.]]> Par défaut Django ajoute automatiquement un champ id à tous les modèles, et le configure pour être un entier qui s’auto incrémente puis le désigne comme la clé primaire. Il est néanmoins possible d’utiliser un autre champ comme clé primaire pour sa table: un slug ou un identifiant métier. Dans notre cas, on va voir comment utiliser un UUID.

Un UUID est un identifiant généré de manière pseudo aléatoire qui a une forte probabilité d’être unique dans le monde entier, il y a 4×10³⁷ combinaisons possibles pour les versions des algos récents. Selon le paradoxe des anniversaires, 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. Normalement pour votre site de fans club des limules hermaphrodites, ça devrait être suffisant.

Utiliser des UUID possède de nombreux avantages car ils sont plus ou moins garantis d’être uniques, et ainsi:

  • Vos serveurs peuvent les générer indépendemment les uns des autres.
  • ./manage.py loadata ne va pas vous crasher à la gueule à cause d’une duplicate key.
  • On peut faire du sharding sur la clé très facilement.
  • Faire des réplications et des synchronisations est beaucoup plus simple qu’avec un AUTO INT.
  • Votre modèle n’a pas besoin d’un champ significatif pour être unique.
  • Vous pouvez générer l’ID côté client et l’envoyer au serveur.
  • Faire communiquer des systèmes complètement séparés, différents ou par API interposée ne pose aucun problème de référence.

Pour toutes ces raisons, les bases de données NoSQL (CouchDB, MongoDB, etc) utilisent depuis longtemps les UUID comme clés primaires par défaut. Le moteur de base de données de Google, Big Table, utilise des UUID.

Pourtant les UUID ne sont pas exempts de défauts:

  • Les JOINS sont beaucoup plus lents.
  • Il prennent plus d’espace disque et en mémoire.
  • On ne peut pas les mettre dans des URL sans que ce soit très très moche.
  • Ils sont difficiles à retenir, dicter et rechercher manuellement.
  • Ils sont non significatifs. Regardez un UUID ne vous donne aucune info sur les données sous-jacentes.
  • Ils ne sont pas ordonnés. Cela a un impact sur la manipulation des données, et sur certains moteurs de BDD à l’insertion.

Les UUID ne sont donc pas la solution miracle à tous les soucis, mais ils sont tout de même mon choix par défaut en ce moment ne serait-ce que pour les fixtures. Je gagne les performances sur le caching et le parallélisme, et un peu de lenteur sur les JOINS est quelque chose que je peux supporter. Pour les gros sites, les JOINS sont de tout façon votre ennemi juré et on finit toujours par tweaker à grand coup de dé-normalisation.

On peut faire passer ses modèles Django aux UUID en faisant:

from uuid import uuid4
 
class MotDElle(object):
    ...
    id = models.CharField(max_length=36, primary_key=True,
                          default=lambda: str(uuid4()), editable=False)

Ou, si vous utilisez django_extensions (et vous devriez, ne serait-ce que pour shell_plus et runserver_plus):

from django_extensions.db.fields import UUIDField
 
class MotDElle(object):
    ...
    id = UUIDField(primary_key=True)

Ce qui a l’avantage de caster la valeur en un objet UUID, et non une bête string, à la lecture.

Malheureusement, pour le moment il n’y a aucune implémentation officielle de Django pour utiliser les UUID. Cela a des conséquences fort ennuyeuses:

  • Les implémentations actuelles utilisent un VARCHAR pour stocker l’UUID au lieu d’un type natif quand c’est possible, et donc ce n’est pas du tout optimisé. L’exception étant django-pdfield, mais dans ce cas, adieu la compatibilité entre plusieurs SGBD.
  • django.contrib.auth.models.User n’a aucun moyen d’utiliser proprement les UUID (il ne vous reste que le monkey patching). Cela pourrait changer avec Django 1.5 qui prévoit un modèle User extensible. \o/

Un ticket pendouille depuis 5 ans sur la question. Je vous invite d’ailleurs à le spammer jusqu’à ce que réouverture s’en suive. Et le premier qui me dit “t’as qu’à l’implémenter, toi”, je lui retourne un tampon dans la poire.

]]>
http://sametmax.com/utiliser-des-uuid-comme-primary-key-avec-lorm-de-django/feed/ 5