L’ID d’une ressource : GUID ou séquentiel ?
(twitter.com/dylayed)Lorsqu’il s’agit de créer un ID permettant d’identifier de manière unique une ressource donnée, je crois qu’en général on utilise surtout deux grandes approches. La première consiste à utiliser telle quelle la valeur entière séquentielle obtenue en appliquant un auto-increment à la clé primaire d’une table de base de données, et la seconde consiste à générer à chaque fois un GUID (également appelé UUID), c’est-à-dire une valeur aléatoire de 128 bits.
Une grande partie des données des nombreux services que l’on voit sur le web repose sur des SGBDR, et l’auto-increment de ces DBMS est non seulement optimisé en interne, mais aussi facile à comprendre et à anticiper du point de vue des développeurs ; il est également simple de trier les données dans leur ordre d’arrivée. Après tout, il ne fait qu’ajouter 1 à un nombre. Mais cette méthode présente aussi des inconvénients : dans certains cas, elle peut exposer à l’extérieur des informations qu’il vaudrait mieux ne pas divulguer pour des raisons de sécurité (par exemple, un concurrent pourrait facilement deviner des indicateurs clés comme le nombre d’utilisateurs de notre service), ou encore poser problème dans une architecture distribuée.
L’approche basée sur les GUID a des caractéristiques exactement opposées. Comme un GUID est une valeur de 128 bits pratiquement unique, générée à la volée avec une probabilité de collision proche de zéro sans autre dépendance, il ne pose aucun problème dans une architecture distribuée et ne risque pas non plus de divulguer à l’extérieur d’autres informations significatives. En revanche, écrire des valeurs générées aléatoirement dans un SGBDR peut entraîner une baisse des performances, et il est en outre impossible, avec elles seules, de trier les données selon leur ordre d’arrivée. Pour compenser ces faiblesses, certains utilisent non pas un aléa total mais des identifiants intégrant des informations temporelles et présentant une séquentialité imparfaite, comme Timeflake. Je ne l’ai jamais utilisé moi-même, mais il paraît que des frameworks comme Laravel adoptent aussi ce type d’approche.
Personnellement, comme je développe dans mon entreprise actuelle des produits intégrés à des services qui utilisent activement les GUID, comme Microsoft Office 365 ou Graph API, j’en suis venu à penser qu’une approche misant davantage sur les GUID n’est pas mauvaise du tout. Mais au final, ce genre de choix dépend toujours du contexte d’utilisation et de l’objectif recherché ; mieux vaut donc bien connaître les avantages et les inconvénients de chaque méthode. C’est pourquoi je vous présente un thread sur Twitter qui tient le journal fictif d’un développeur de service à ce sujet. (coréen)
15 commentaires
Un récent cas d’utilisation frauduleuse est survenu chez Shinhan Card, et il a mis en évidence le risque d’exposition à des fraudes depuis l’étranger lié au fait que l’émetteur attribuait les numéros de carte bancaire de manière séquentielle.
Ils n’ont modifié que légèrement le numéro, et le « paiement » est passé… des cartes bancaires exposées au vol
Le Service de supervision financière prépare des mesures face aux récents usages frauduleux chez Shinhan Card
Grâce aux commentaires de beaucoup d'entre vous, j'ai appris énormément de choses que je ne connaissais pas bien.
J'ai ainsi découvert pour la première fois des choses comme Hashids, Nano ID ou encore la méthode utilisée par Instagram.
La motivation est probablement similaire à celle d’ulid, mais comme il existe un Internet Draft en cours de proposition, j’avais utilisé cette spécification dans un précédent projet.
https://github.com/uuid6/uuid6-ietf-draft
On voit souvent des systèmes d’ID générés de cette façon, mais il semble qu’on soit arrivé au moment où il faudrait au moins unifier les variantes de type UUID en une seule.
Mais on dit souvent que la tentative de créer une nouvelle norme pour unifier en une seule toutes les normes qui prolifèrent ne fait généralement qu’ajouter un nouveau concurrent sur le marché. lol
https://xkcd.com/927/
C’est ça, haha, donc on dirait que tout le monde propose de nouveaux ID.
Récemment, Gyuwon l’avait partagé, mais en réalité, n’est-ce pas une question assez simple ?
https://byterot.blogspot.com/2013/02/…
Moi aussi, j’aimerais une explication complémentaire sur le fait que ce serait un « problème simple ».
En quel sens dites-vous que c’est une question simple ?
Cet article dit bien : « With storage nowadays very cheap, this normally is not a problem from the storage point of view. », mais il me semble qu’il existe aussi des situations où il faut refuser les UUID selon le contexte : par exemple quand cet ID doit circuler sur le réseau, quand il doit servir de clé en mémoire, ou encore quand il s’agit d’une clé utilisée à plusieurs endroits sur de gros volumes de données, et où il est important d’économiser ne serait-ce que quelques octets.
Le problème évoqué dans cet article est la dégradation des performances lorsqu’on utilise comme clé primaire une valeur générée aléatoirement
(s’il y a un autre problème mentionné et que je ne l’ai pas relevé, dites-le-moi)
Ce problème a déjà une réponse. C’est le même problème que pour la pagination basée sur des curseurs en ordre chronologique, donc j’imagine que tout le monde l’a déjà résolu.
Je me demande aussi en quoi c’est un problème complexe sur certains aspects.
Puisque vous dites que, dans la situation évoquée, il faut refuser, j’ai l’impression que c’est un problème simple...
Un problème complexe, ce n’est pas justement quand on n’arrive à prendre une décision ni dans un sens ni dans l’autre ?
J’ai posé la question parce que l’expression « problème simple » peut être interprétée de plusieurs façons, et je me demandais dans quel sens vous l’employiez. Vouliez-vous dire que le problème en lui-même n’est pas difficile, ou bien que l’article apporte une réponse claire (?) qui laisse peu de place à l’hésitation, par exemple ?
Concernant le premier point, comme je l’ai dit plus haut, selon la situation, l’ID peut devoir être utilisé aussi en dehors de la base de données ; il y a donc davantage d’éléments à prendre en compte, et je ne pense pas que ce soit une question simple.
Il y a aussi ceci pour python/django : https://pypi.org/project/django-hashid-field/1.0.0/
Oh, il existe aussi une méthode appelée Hashids.
Si le salt venait à fuiter, cela pourrait entraîner le problème d’exposition d’informations vers l’extérieur mentionné dans l’article principal, mais je pense que cela reste malgré tout une bonne méthode.
Il y a aussi ULID. 128 bits, triable par ordre chronologique.
https://github.com/ulid/spec
Quand on voit à quel point beaucoup de choses se ressemblent, on se dit que les gens finissent souvent par avoir les mêmes idées… ?