Les UUID et les ULID sont des mécanismes servant à délivrer des « identifiants uniques » dans les systèmes distribués et les bases de données. Ils se ressemblent en apparence, mais diffèrent par la normalisation, l’efficacité de stockage, la lisibilité pour l’être humain ou encore l’exposition d’informations sensibles. Ci-dessous, je dresse un panorama des trois formats les plus représentatifs — UUID v4, UUID v7 et ULID — puis je termine par un complément sur UUID v1 / v6.

Voici les outils qui fonctionnent entièrement dans le navigateur pour générer chaque ID et, le cas échéant, extraire l’horodatage :


UUID v4 : génération entièrement aléatoire

UUID v4 consacre 122 bits sur 128 au hasard et se distingue par une probabilité de collision extrêmement faible. En contrepartie, il ne respecte pas l’ordre chronologique, ce qui provoque des divisions de pages fréquentes dans les index de type B-Tree et dégrade la localité d’insertion. Il est donc adapté aux identifiants de session ou aux clés à usage unique où le caractère purement aléatoire est souhaitable.


UUID v7 : l’évolution standard triable par date

Normalisé en 2024 par la RFC 9562, UUID v7 encode les 48 premiers bits avec l’heure UNIX en millisecondes et remplit le reste avec du hasard. L’ordre lexicographique (comparaison de chaînes) correspond à l’ordre de génération (chronologique), ce qui assure une bonne localité pour un index primaire. Autre avantage : il se stocke tel quel dans un type uuid ou BINARY(16) existant.

  • À propos du nombre de bits aléatoires : mis à part quelques bits réservés à la version et à la variante, v7 offre environ 74 bits de hasard. Certaines implémentations appliquent une génération monotone (réajustement partiel du hasard pour éviter les collisions au sein de la même milliseconde), mais l’aléa reste statistiquement suffisant.
  • Risque d’exposition d’information : à partir d’un ID, on peut déduire l’instant de génération (précision milliseconde). Si l’on émet beaucoup d’identifiants et que l’implémentation aléatoire est faible, il devient relativement plus facile d’extrapoler des IDs voisins. Cela reste généralement acceptable en interne, mais il faut être vigilant lorsqu’on ne souhaite pas révéler une séquence quasi séquentielle via un point d’accès public.

ULID : un identifiant convivial pour l’humain

ULID est un standard de facto proposé en 2016. Il s’écrit en 26 caractères Base32 de Crockford, sans les lettres ambiguës (O/I/L/1), ce qui le rend fiable pour un copier-coller ou une insertion d’URL. Comme UUID v7, il place les 48 premiers bits pour l’heure (ms), de sorte que l’ordre lexicographique correspond à l’ordre chronologique.

  • Garantie de tri : comparer deux chaînes ULID en Base32 revient toujours à les classer par ordre temporel (la génération monotone est largement répandue).
  • Mode de stockage : l’encodage en TEXT(26) reste lisible pour un humain, mais un stockage binaire en base de données nécessite une conversion Base32 ↔ 16 octets. On peut le traiter comme un binaire de 128 bits, mais il est souvent incompatible avec les types UUID dédiés des SGBD.
  • Risque d’exposition d’information : comme v7, il révèle l’heure (ms). Pour un ID exposé publiquement où l’on veut éviter de dévoiler l’ordre d’émission, il faut en peser les implications.

Tableau comparatif (perspective pratique)

Élément UUID v4 UUID v7 ULID
Normalisation RFC 4122 RFC 9562 (2024) Non normalisé (de facto)
Structure des bits 128 bits dont 122 bits de hasard 48 bits = UNIX ms + reste aléatoire (monotone possible) 48 bits = UNIX ms + 80 bits aléatoires
Entropie aléatoire ≈ 122 bits ≈ 74 bits (estimation hors version/variante) 80 bits
Représentation textuelle 36 caractères (hex + tirets) 36 caractères (hex + tirets) 26 caractères (Base32 Crockford)
Tri naturel (texte) × (aléatoire) ○ (lexicographique = chronologique) ○ (lexicographique = chronologique)
Tri naturel (binaire) × ○ (ordre croissant avec BINARY(16) en big-endian) ○ (ordre croissant via memcmp si les 6 octets de temps sont en tête big-endian)
Stockage en base Type uuid / BINARY(16) optimal Type uuid / BINARY(16) optimal TEXT(26) lisible, BINARY(16) nécessite conversion / incompatible avec les types UUID
Localité d’index Mauvaise (insertions aléatoires fragmentent) Bonne (appends en queue / attention aux points chauds) Bonne (appends en queue / attention aux points chauds)
Coût d’égalité Faible (comparaison sur 16 octets) Faible (comparaison sur 16 octets) TEXT plus coûteux (collation) / BINARY faible
Charge d’encodage Hex ↔ 16 octets (léger) Hex ↔ 16 octets (léger) Base32 ↔ 16 octets (plus lourd)
Lisibilité humaine / URL Faible Faible Élevée
Exposition d’horodatage Aucune Oui (ms) Oui (ms)
Cas d’usage principaux ID de session, clés à usage unique Clé primaire en BD, ID d’événement, logs chronologiques ID publics / URL, visibilité des logs

Complément (points chauds v7/ULID) : si l’on concentre les écritures en append vers un unique shard ou un leader unique, l’extrémité du B-Tree peut devenir un hot spot. En prévention, envisagez un léger shuffle des bits de tête (« prefix shuffle ») ou un sharding supplémentaire.


Mémo de mise en œuvre (PostgreSQL / MySQL / SQLite)

  • PostgreSQL
    • v4/v7 : le type uuid est idéal. Insérez v7 sous forme de chaîne → uuid pour bénéficier du tri.
    • ULID : utilisez char(26)/text ou bytea(16) (conversion Base32).
  • MySQL/InnoDB
    • BINARY(16) est rapide et compact (v4/v7). v7 reste trié naturellement en conservant l’endian big d’origine. ULID : CHAR(26) ou BINARY(16) + conversion.
  • SQLite
    • Pas de type UUID dédié. Utilisez BLOB(16) ou TEXT. Un BLOB indexé se compare efficacement.

Renforcer le volet sécurité

  • Résistance à la prédiction : v7/ULID partagent des bits d’horodatage ; si vous émettez un gros volume par milliseconde avec un PRNG faible, le risque d’extrapoler des IDs proches augmente. Utilisez un PRNG cryptographiquement sûr et réduisez au maximum les biais introduits par la génération monotone.
  • Fuite de métadonnées : on peut déduire l’instant de génération et un ordre de grandeur du volume émis. Pour une API publique, séparez idéalement ID interne et ID exposé.

Synthèse : critères de choix

  • UUID v4 : purement aléatoire. Pas de tri naturel. Il subsiste une probabilité théorique de collision, mais infime. Préférez-le pour les ID de session ou les jetons CSRF.
  • UUID v7 : UUID ordonné et standardisé. Excellent choix pour les systèmes internes et les clés primaires. Avec une génération monotone, le risque de collision devient négligeable en pratique.
  • ULID : plus convivial pour l’humain. Idéal lorsque la lisibilité (URL, journaux) prime, mais souvent moins pertinent que v7 pour des clés primaires internes. La génération monotone maintient un risque de collision négligeable en pratique.

Complément : UUID v1 / v6 (en bref)

  • UUID v1 (timestamp + MAC) Combine un horodatage sur 60 bits (pas de 100 ns) et un identifiant de nœud (souvent l’adresse MAC). Il offre une forte temporalité, mais révèle potentiellement des informations sur l’hôte via la MAC, avec de nombreuses précautions d’implémentation (horloge qui recule, etc.). Pour des raisons de confidentialité, on le déconseille souvent aujourd’hui.

  • UUID v6 (réordonnancement de v1) Réorganise l’horodatage de v1 en big-endian pour améliorer le tri lexicographique. Historiquement perçu comme un « v1 facile à trier », mais la normalisation a finalement convergé vers v7. Dans un nouveau design, on privilégie v7 plutôt que v6.


Annexe : liste de contrôle à l’implémentation

  • Utilisez un PRNG cryptographique fourni par l’OS.
  • La génération monotone de v7/ULID évite les collisions dans la même milliseconde tout en limitant les biais.
  • Préférez les types uuid/BINARY(16) en base, afin d’éviter les dégradations dues aux collations de colonnes texte.
  • Décidez d’une politique pour les ID externes (exposition de l’ordre). Séparez si nécessaire ID internes et externes.

En résumé, d’un point de vue technique, UUID v7 doit être votre premier choix pour une clé primaire interne, tandis que ULID reste pertinent lorsque la lisibilité externe ou certaines contraintes techniques le justifient. On ne réservera v4 ou d’autres formats qu’aux exigences particulières (interdiction d’exposer l’heure, etc.).