UUID v4 vs v7 vs ULID : comment choisir le bon identifiant pour votre base de données
Vous démarrez un nouveau service, et la première décision est d’une simplicité trompeuse : à quoi doit ressembler votre clé primaire ? Les entiers auto-incrémentés révèlent le nombre de lignes, exposent l’ordre de création et posent problème dès qu’il faut fusionner des données entre bases. Les UUID résolvent ces problèmes — mais encore faut-il choisir lequel.
UUID v4 est la norme par défaut depuis plus de dix ans. UUID v7, standardisé dans le RFC 9562 en mai 2024, promet de meilleures performances en base de données grâce à un tri chronologique. Et ULID, l’alternative communautaire née en 2016, offre des avantages similaires dans un format plus compact. Chacun présente de vrais compromis qui impactent les performances, la confidentialité et la maintenabilité à long terme.
Ce guide compare les trois — avec des benchmarks réels, des conseils pratiques de migration et un cadre de décision clair pour 2026.
Qu’est-ce qu’un UUID ? (Et pourquoi « GUID », c’est la même chose)
Un UUID (Universally Unique Identifier) est une valeur de 128 bits conçue pour être unique sans autorité centrale. Le format standard est composé de 32 caractères hexadécimaux séparés par des tirets : 550e8400-e29b-41d4-a716-446655440000.
Si vous avez travaillé avec .NET ou Windows, vous les avez vus sous le nom de GUID (Globally Unique Identifier). C’est exactement la même chose — GUID est le nom donné par Microsoft au standard UUID. La disposition des bits, les algorithmes de génération et les besoins en stockage sont identiques.
L’UUID a connu plusieurs versions. Celles qui comptent aujourd’hui :
| Version | Stratégie | Standardisé | Encore pertinent ? |
|---|---|---|---|
| v1 | Horodatage + adresse MAC | RFC 4122 (2005) | Largement remplacé par v7 |
| v4 | Aléatoire | RFC 4122 (2005) | Oui — la norme actuelle |
| v5 | Basé sur un nom (hash SHA-1) | RFC 4122 (2005) | Cas d’usage de niche |
| v7 | Horodatage + aléatoire | RFC 9562 (2024) | Oui — la nouvelle recommandation |
Le RFC 9562 est explicite sur la direction à prendre : « Implementations SHOULD utilize UUIDv7 instead of UUIDv1 and UUIDv6 if possible. »
UUID v4 : le standard aléatoire
UUID v4 génère des identifiants à partir de 122 bits d’aléatoire cryptographiquement sécurisé. Cela représente un espace de clés d’environ 5,3 × 10³⁶ valeurs possibles — il faudrait générer un milliard d’UUID par seconde pendant 86 ans pour atteindre une probabilité de collision de 50 %.
Fonctionnement : 128 bits au total, dont 6 bits réservés aux marqueurs de version (4) et de variante. Les 122 bits restants proviennent d’un CSPRNG comme crypto.getRandomValues().
Exemple : f47ac10b-58cc-4372-a567-0e02b2c3d479
La simplicité est sa force. Aucune coordination requise, pas d’horodatage à divulguer, pas d’état à maintenir. Chaque langage et base de données majeurs disposent d’un support natif de v4.
Le problème : les UUID aléatoires fragmentent votre base de données
Le caractère purement aléatoire a un coût. Lorsque vous insérez des UUID aléatoires dans un index B-tree — le type par défaut pour PostgreSQL, MySQL et la plupart des bases relationnelles — les nouvelles lignes atterrissent à des positions arbitraires dans l’arbre. Cela provoque :
- Des divisions de pages : la base de données divise et réorganise constamment les pages d’index au lieu d’ajouter à la fin.
- Une amplification des écritures : les benchmarks d’EnterpriseDB montrent que les UUID aléatoires génèrent 8 fois plus de WAL (write-ahead log) que les alternatives séquentielles — 20 Go contre 2,5 Go pour la même charge de travail.
- Un effondrement du débit : sur des jeux de données dépassant la RAM disponible, les insertions d’UUID aléatoires tombent à 20–30 % du débit des UUID séquentiels.
- Un gaspillage d’espace : PlanetScale rapporte que les UUID aléatoires font chuter l’utilisation des pages B+ tree de MySQL à environ 50 %, contre 94 % avec des clés séquentielles.
Ce n’est pas une préoccupation théorique. Buildkite a constaté une réduction de 50 % du taux de WAL sur sa base de données principale après le passage aux UUID ordonnés chronologiquement. Shopify a mesuré une diminution de 50 % de la durée des INSERT en passant d’UUID v4 à des identifiants ordonnés chronologiquement pour les clés d’idempotence de son système de paiement.
UUID v4 convient pour les petites tables, les écritures peu fréquentes, ou partout où l’UUID n’est pas utilisé comme clé primaire. Pour les systèmes à haut débit, la taxe de fragmentation s’accumule au fil du temps.
UUID v7 : ordonné chronologiquement et optimisé pour les bases de données
UUID v7 résout le problème de fragmentation en plaçant un horodatage en premier. Les 48 bits les plus significatifs encodent un horodatage UNIX en millisecondes, suivis d’environ 74 bits d’aléatoire cryptographiquement sécurisé.
Fonctionnement :
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
├─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤
| unix_ts_ms (48 bits) |
├─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤
| unix_ts_ms | ver | rand_a (12 bits) |
├─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤
|var| rand_b (62 bits) |
├─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤─┤
Comme les UUID générés dans la même milliseconde partagent un préfixe d’horodatage, ils se regroupent dans les index B-tree. Les nouvelles insertions s’ajoutent près de la fin de l’arbre plutôt que de se disperser aléatoirement — le même schéma d’accès qui rend les entiers auto-incrémentés performants.
Exemple : 019078e5-d2c7-7b3a-8f1e-4a6d5c8b2e91 — les 12 premiers caractères hexadécimaux encodent l’heure de création.
Ce que le RFC 9562 a changé
Avant le RFC 9562, les UUID ordonnés chronologiquement n’existaient que sous forme de propositions informelles. Le RFC, publié en mai 2024, a fait d’UUID v7 un standard officiel de l’IETF avec une disposition de bits définie. Il répertorie 16 implémentations non standard d’identifiants ordonnés chronologiquement — dont ULID, Snowflake de Twitter et ShardId d’Instagram — qui ont motivé sa création.
L’impact pratique : les auteurs de bibliothèques disposent désormais d’une spécification unique à implémenter, et les bases de données ajoutent un support natif. PostgreSQL 18 (sorti fin 2025) inclut une fonction native uuidv7(), éliminant le besoin d’extensions ou de génération côté application.
La fonction native uuidv7() de PostgreSQL 18
PostgreSQL 18, sorti fin 2025, ajoute deux nouvelles fonctions :
-- Générer un UUID v7 ordonné chronologiquement
SELECT uuidv7();
-- Résultat : 019078e5-d2c7-7b3a-8f1e-4a6d5c8b2e91
-- Générer un UUID v4 aléatoire (alias de gen_random_uuid())
SELECT uuidv4();
L’implémentation de PostgreSQL ajoute une fraction d’horodatage sub-milliseconde de 12 bits, offrant un meilleur ordonnancement monotone au sein d’un même processus backend. Cela signifie que même les lignes insérées dans la même milliseconde conservent leur ordre de création — un point important pour les tables à haut débit.
Pour les versions antérieures de PostgreSQL, vous pouvez générer les UUID v7 côté application ou utiliser des extensions comme pg_idkit.
UUID v4 vs v7 : comparaison directe
| Propriété | UUID v4 | UUID v7 |
|---|---|---|
| Format | 128 bits, 36 caractères hex | 128 bits, 36 caractères hex |
| Bits aléatoires | 122 | ~74 |
| Triable par date de création | Non | Oui (précision ms) |
| Performance d’index B-tree | Mauvaise à grande échelle | Excellente |
| Fuite d’horodatage | Aucune | Oui (précision ms) |
| Standard RFC | RFC 4122 / RFC 9562 | RFC 9562 (mai 2024) |
| Support natif en base de données | Toutes les bases majeures | PostgreSQL 18+, en expansion |
| Support des bibliothèques | Universel | Large et en expansion |
| Taille de stockage | 16 octets (binaire) | 16 octets (binaire) |
| Compatibilité de type de colonne | uuid / BINARY(16) | uuid / BINARY(16) — même colonne, entièrement compatible |
Le point essentiel : v4 et v7 sont compatibles au niveau des colonnes. Ils utilisent le même type de données uuid, le même stockage binaire de 16 octets, la même représentation en chaîne de 36 caractères. Vous pouvez stocker les deux dans la même colonne, ce qui facilite la migration progressive.
Essayez par vous-même : UUID Generator — générez des identifiants UUID v4 instantanément dans votre navigateur, avec génération en lot et copie en un clic.
UUID v7 vs ULID : avez-vous encore besoin d’ULID ?
ULID (Universally Unique Lexicographically Sortable Identifier) a été lancé en 2016 pour résoudre le même problème qu’UUID v7 aujourd’hui : des identifiants uniques globalement et ordonnés chronologiquement. Voici comment ils se comparent :
| Propriété | UUID v7 | ULID |
|---|---|---|
| Encodage | 36 caractères hex avec tirets | 26 caractères Crockford Base32 |
| Horodatage | 48 bits ms epoch | 48 bits ms epoch |
| Bits aléatoires | ~74 | 80 |
| Standard | IETF RFC 9562 | Spécification communautaire (pas de RFC) |
| Ordonnancement monotone | Dépend de l’implémentation | Incrément défini dans la spécification au sein de la même ms |
| Type en base de données | Colonne native uuid | Nécessite CHAR(26) ou BINARY(16) |
| Compatible URL | Non (tirets) | Oui (Base32) |
| Horodatage valide jusqu’en | Année 10889 | Année 10889 |
Là où UUID v7 l’emporte
-
Standardisation. UUID v7 dispose d’un RFC de l’IETF. Chaque base de données, ORM et runtime de langage comprend déjà le type UUID. ULID nécessite un parsing personnalisé dans la plupart des écosystèmes.
-
Support natif en base de données. Les ULID ne rentrent pas dans la colonne native
uuidde PostgreSQL sans conversion. Il faut soit les stocker comme chaînes de caractères (gaspillage d’espace), soit les convertir en binaire (perte de l’encodage Crockford). UUID v7 s’intègre directement dans les colonnesuuidexistantes. -
Dynamique de l’écosystème. Bytebase prédit que l’industrie va « progressivement abandonner les solutions sur mesure et converger vers UUIDv7 comme clé primaire pour la plupart des cas d’usage ». La fonction native
uuidv7()de PostgreSQL 18 accélère cette tendance.
Là où ULID reste pertinent
-
Représentation compacte. Avec 26 caractères contre 36, les ULID sont plus courts dans les URL et les API. Si la longueur de la chaîne compte pour votre cas d’usage, ULID est plus compact.
-
Un peu plus d’aléatoire. ULID fournit 80 bits aléatoires contre environ 74 pour UUID v7. En pratique, les deux ont des taux de collision astronomiquement faibles, mais ULID a une composante aléatoire marginalement plus grande.
-
Infrastructure ULID existante. Si votre système utilise déjà des ULID et que le coût de migration est significatif, il n’y a pas de raison urgente de changer. Les deux offrent des performances de base de données comparables puisqu’ils partagent le même préfixe d’horodatage de 48 bits.
Pour les nouveaux projets en 2026, UUID v7 est le choix le plus sûr. Il est standardisé, nativement supporté par les bases de données, et ne demande pas à votre équipe de maintenir une logique de parsing ULID.
Performances en base de données : benchmarks réels
La différence de performance entre identifiants aléatoires et ordonnés chronologiquement est bien documentée à travers des systèmes en production :
PostgreSQL
| Métrique | UUID v4 | UUID v7 / Séquentiel |
|---|---|---|
| Débit INSERT (grand jeu de données) | 20–30 % de la référence | 100 % de la référence |
| Volume WAL | ~20 Go | ~2,5 Go |
| Taux de cache hit | 85 % | 99 % |
Source : benchmark EnterpriseDB. L’écart se creuse à mesure que les jeux de données dépassent la RAM disponible.
MySQL / InnoDB
Le moteur InnoDB de MySQL utilise des index clusterisés, où la clé primaire détermine l’ordre physique des lignes. Les UUID aléatoires sont particulièrement coûteux :
- L’utilisation des pages B+ tree chute à environ 50 % (contre 94 % en séquentiel)
- Un UUID stocké en
CHAR(36)est 9 fois plus grand qu’un entier 32 bits BINARY(16)est le format de stockage recommandé — 4 fois plus grand qu’un entier, mais suffisamment compact pour la plupart des charges de travail
Source : analyse PlanetScale.
Quand conserver l’auto-incrémentation
Les UUID ne sont pas toujours la réponse. Les entiers auto-incrémentés restent le bon choix quand :
- Vous exploitez une base de données unique sans projet de sharding
- Le débit d’écriture compte plus que l’unicité globale
- Le nombre de lignes n’est pas une information sensible
- Vous n’avez pas besoin de générer des identifiants en dehors de la base de données
Pour les systèmes distribués, les microservices, ou toute architecture où les identifiants doivent être générés côté application, UUID v7 vous offre à la fois les performances des clés séquentielles et la flexibilité de la génération décentralisée.
Comment générer des UUID v7
JavaScript / TypeScript
// Using the 'uuid' package (v10+)
import { v7 as uuidv7 } from 'uuid';
const id = uuidv7(); // '019078e5-d2c7-7b3a-8f1e-4a6d5c8b2e91'
Python
# Python 3.x with uuid7 package
from uuid_extensions import uuid7
id = str(uuid7()) # '019078e5-d2c7-7b3a-8f1e-4a6d5c8b2e91'
Go
// Using github.com/gofrs/uuid/v5
import "github.com/gofrs/uuid/v5"
id, _ := uuid.NewV7()
fmt.Println(id.String()) // "019078e5-d2c7-7b3a-8f1e-4a6d5c8b2e91"
PostgreSQL 18+
-- Native function, no extension needed
CREATE TABLE orders (
id uuid PRIMARY KEY DEFAULT uuidv7(),
customer_id uuid NOT NULL,
created_at timestamptz DEFAULT now()
);
Autres langages
Des bibliothèques UUID v7 sont disponibles pour Rust (crate uuid v1.4+), Java (uuid-creator), C# (UUIDNext) et PHP (symfony/uid).
Migrer d’UUID v4 vers v7
Vous utilisez déjà UUID v4 en production ? Bonne nouvelle : la migration ne nécessite pas un basculement total.
v4 et v7 peuvent coexister
Les deux versions partagent le même type de colonne uuid et la même représentation binaire de 16 octets. Vous pouvez commencer à générer des v7 pour les nouvelles lignes tout en conservant les lignes v4 existantes. Les requêtes, les jointures et les index fonctionnent de manière identique — la base de données ne se soucie pas de la version d’un UUID.
La seule différence de comportement : les nouvelles lignes v7 se trieront après les anciennes lignes v4 lorsqu’elles sont ordonnées par clé primaire, puisque le préfixe d’horodatage de v7 les place plus tard dans l’ordre lexicographique. Si votre application repose sur l’ordonnancement des UUID (ce qui ne devrait pas être le cas avec v4), vérifiez cette hypothèse.
Checklist de migration
- Mettez à jour la génération d’identifiants — basculez la génération d’UUID côté application vers v7. Pour PostgreSQL 18+, changez la valeur par défaut de la colonne en
uuidv7(). - Mettez à jour les ORM — la plupart des ORM délèguent la génération d’UUID à l’application ou à la base de données. Mettez à jour le générateur, pas le type de colonne.
- Surveillez les performances des index — après le basculement, les nouvelles insertions seront séquentielles. Au fil du temps, vos index deviendront naturellement plus efficaces à mesure que les lignes v7 domineront.
- Ne faites pas de backfill — convertir les valeurs v4 existantes en v7 est inutile et casserait les références de clés étrangères. Laissez v4 et v7 coexister.
Considération de confidentialité : les horodatages dans vos identifiants
UUID v7 encode l’heure de création avec une précision à la milliseconde. Quiconque peut voir l’UUID peut en extraire le moment de sa création. Pour les clés de base de données internes, c’est rarement un problème. Mais considérez les implications pour :
- Les API publiques — si votre API expose les identifiants d’entités, les clients peuvent déterminer quand les ressources ont été créées. Cela pourrait révéler des tendances métier (volume de commandes, taux de croissance des utilisateurs).
- Les jetons de session et clés API — préférez UUID v4 pour les secrets. L’horodatage dans v7 n’apporte aucune valeur pour les jetons et divulgue inutilement des informations temporelles.
- La conformité réglementaire — selon votre juridiction, les horodatages intégrés dans les identifiants pourraient constituer des données personnelles s’ils peuvent être rattachés à un utilisateur.
Une approche pragmatique : utilisez UUID v7 pour les clés primaires de base de données (usage interne), UUID v4 pour les jetons et clés API visibles de l’extérieur.
Essayez par vous-même : UUID Generator — besoin d’un lot d’UUID pour vos tests ? Générez-en jusqu’à 25 à la fois, copiez individuellement ou téléchargez en fichier texte.
Questions fréquentes
Deux UUID peuvent-ils être identiques ?
En théorie oui, mais en pratique non. Les 122 bits aléatoires d’UUID v4 offrent un espace de clés de 5,3 × 10³⁶. Il faudrait générer un milliard d’UUID par seconde pendant 86 ans pour atteindre une probabilité de collision de 50 %. UUID v7 a moins de bits aléatoires (~74), mais les collisions au sein de la même milliseconde restent astronomiquement improbables.
Faut-il stocker les UUID en chaîne de caractères ou en binaire ?
En binaire. Un UUID stocké en CHAR(36) occupe 36 octets ; en BINARY(16), il n’en occupe que 16 — moins de la moitié du stockage. Le type natif uuid de PostgreSQL stocke automatiquement en binaire sur 16 octets. Sous MySQL, utilisez BINARY(16) et convertissez côté application. PlanetScale souligne que CHAR(36) est 9 fois plus grand qu’un entier 32 bits, rendant le stockage binaire indispensable pour les grandes tables.
Comment UUID v7 se compare-t-il à Twitter Snowflake ?
Les deux sont des identifiants distribués ordonnés chronologiquement, mais ils répondent à des besoins différents. Les identifiants Snowflake sont en 64 bits (plus petits, plus rapides à comparer), mais nécessitent un service de coordination central pour attribuer les identifiants de worker. UUID v7 est en 128 bits et ne nécessite aucune coordination — n’importe quel noeud peut en générer de manière indépendante. Le RFC 9562 liste explicitement Snowflake parmi les 16 implémentations non standard qui ont motivé la création d’UUID v7.
Comment générer des UUID v7 dans PostgreSQL avant la version 18 ?
Avant le support natif, plusieurs options s’offrent à vous : l’extension pg_idkit, la génération côté application (générez dans votre code applicatif et transmettez à la base de données), ou une fonction PL/pgSQL qui assemble manuellement l’horodatage et les octets aléatoires. La mise à niveau vers PostgreSQL 18 est la voie la plus propre si votre infrastructure le permet.
UUID v7 est-il sûr pour les clés API ?
Non — utilisez UUID v4 pour les secrets. L’horodatage d’UUID v7 révèle quand la clé a été créée, ce qui est une information superflue dans un contexte de sécurité. Pour les clés API et les jetons de session, vous voulez une entropie maximale sans métadonnées intégrées. Les 122 bits de pur aléatoire d’UUID v4, issus d’un CSPRNG, conviennent, bien que des formats de jetons dédiés puissent offrir des fonctionnalités supplémentaires comme l’expiration intégrée.
Lequel choisir ? Un cadre de décision
Choisissez UUID v4 quand :
- Vous avez besoin d’un maximum d’aléatoire (jetons, clés API, identifiants de session)
- L’heure de création doit rester confidentielle
- Vous ajoutez des UUID à une petite table avec peu d’écritures, où la fragmentation d’index n’a pas d’importance
Choisissez UUID v7 quand :
- L’UUID sert de clé primaire en base de données
- Vous avez besoin d’un tri chronologique sans colonne
created_atsupplémentaire - Le débit d’écriture compte à grande échelle
- Vous voulez les avantages des clés séquentielles sans autorité centrale
Choisissez ULID quand :
- Vous avez besoin d’une représentation en chaîne plus courte (26 caractères contre 36)
- Votre système repose déjà sur une infrastructure ULID
- Vous avez besoin d’identifiants compatibles URL sans encodage
Pour la plupart des nouveaux projets en 2026, UUID v7 est la recommandation par défaut. Il combine la génération décentralisée d’UUID v4 avec les performances en base de données des clés séquentielles, soutenu par un standard IETF et un support natif croissant des bases de données. La convergence vers v7 est bien amorcée — la fonction native uuidv7() de PostgreSQL 18 en est le signal le plus clair.
Quel que soit votre choix, vous pouvez générer des identifiants UUID v4 instantanément avec le UUID Generator — ou formater vos réponses API contenant des UUID avec le JSON Formatter.