12k
All articles

UUID v4 ou v7 : lequel choisir ?

UUID v4 vs v7 pour les clés primaires de base de données : pourquoi v7 améliore les insertions, quand v4 protège la vie privée, et comment les générer.

OpenReplay Team
OpenReplay Team
UUID v4 ou v7 : lequel choisir ?

Pour une nouvelle clé primaire de base de données en 2026, choisissez UUID v7 par défaut et ne recourez à v4 que lorsque l’identifiant est public et que sa date de création doit rester confidentielle.

Si vous avez déjà vu la latence d’insertion grimper sur une table très sollicitée avant d’en identifier la cause — une clé primaire qui atterrit chaque fois dans un coin différent de l’index —, cette question vous semblera familière. Le correctif s’avère bien plus léger qu’une migration de schéma. Les deux formats sont des UUID de 128 bits normalisés par la même RFC : le choix ne porte donc ni sur la compatibilité ni sur la sécurité face aux collisions. Tout se résume à une seule propriété : vos identifiants sont-ils triés dans l’ordre de création ? L’ordonnancement temporel de v7 corrige la fragmentation d’index provoquée par les clés v4 aléatoires sur les tables à forte charge d’écriture, au prix d’un horodatage lisible intégré à chaque valeur. Ce guide décompose la différence structurelle, le mécanisme d’index de base de données qui explique les performances d’écriture de v7, le compromis en matière de confidentialité, la prise en charge actuelle par les bibliothèques, et une recommandation par défaut sans ambiguïté.

Points clés à retenir

  • Les UUID v4 et v7 sont tous deux des identifiants de 128 bits (16 octets) normalisés par la RFC 9562 (mai 2024) ; v7 remplace les 48 premiers bits par un horodatage Unix en millisecondes, de sorte que ses valeurs se trient dans l’ordre de création, tandis que v4 est entièrement aléatoire.
  • Les clés v4 aléatoires se dispersent dans un index B-tree et provoquent des divisions de pages (page splits), un renouvellement constant du cache et de la fragmentation ; le préfixe ordonné dans le temps de v7 fait que les insertions s’ajoutent près de la fin de l’index, avec un comportement bien plus proche de celui d’une clé séquentielle.
  • Un identifiant v7 révèle sa propre date de création à la milliseconde près ; un identifiant v4 ne le fait pas. Utilisez v4 pour les identifiants publics, les liens d’invitation, ou partout où votre taux de croissance doit rester confidentiel.
  • La prise en charge de v7 est de premier ordre : PostgreSQL 18 fournit nativement uuidv7(), Python 3.14 a ajouté uuid.uuid7(), et le paquet JavaScript uuid (v14.x) exporte v7().
  • Aucun UUID n’est un secret : utilisez un jeton aléatoire dédié de 256 bits pour l’authentification et réservez les UUID à l’identité.

Quelle est la différence entre UUID v4 et v7 ?

Les UUID v4 et v7 sont tous deux des identifiants de 128 bits (16 octets) normalisés par la RFC 9562, publiée en mai 2024 en tant que document Standards-Track qui rend obsolète l’ancienne RFC 4122. La seule différence structurelle réside dans l’origine des bits. L’UUID v4 comporte 122 bits d’aléa, avec 6 bits réservés aux marqueurs de version et de variante, ce qui le rend entièrement aléatoire et non ordonné. L’UUID v7 remplace les 48 premiers bits par un horodatage Unix en millisecondes et remplit les ~74 bits restants avec de l’aléa (plus les marqueurs de version et de variante) : les valeurs v7 se trient donc dans l’ordre de création, lexicalement et octet par octet, ce que les valeurs v4 ne permettent pas.

UUID v4:  [ 122 random bits ...................... ] + version/variant
UUID v7:  [ 48-bit ms timestamp ][ ~74 random bits ] + version/variant

Ce seul changement résume toute la décision. Les deux conservent le même calcul de collision issu de la partie aléatoire, les deux tiennent dans la même colonne, et les deux relèvent de la même norme.

Pourquoi UUID v7 l’emporte-t-il pour les clés de base de données ?

v7 surpasse v4 comme clé primaire parce que son préfixe d’horodatage confère aux insertions une localité d’index que les clés aléatoires détruisent. Les clés v4 aléatoires atterrissent à des positions arbitraires dans un index B-tree, provoquant des divisions de pages, un renouvellement du cache et de la fragmentation, puisque chaque insertion cible une partie différente de l’arbre. C’est précisément le problème que la RFC 9562 invoque comme justification de la définition de v7 : lorsque les identifiants ne portent aucun ordonnancement temporel, chaque nouvelle ligne doit être écrite là où sa valeur aléatoire tombe, alors que des valeurs générées les unes après les autres selon un schéma ordonné dans le temps se retrouvent voisines dans l’index. Le préfixe monotone de v7 signifie que les nouvelles lignes s’ajoutent près de la fin de l’index : les divisions de pages deviennent rares et le débit d’insertion se rapproche de celui d’une clé entière séquentielle, tout en conservant la sécurité face aux collisions et la génération distribuée propres aux UUID.

La pénalité est la plus lourde sur les moteurs à index clustered. Sous MySQL InnoDB et SQL Server, la table est physiquement ordonnée selon la clé primaire : les insertions aléatoires réécrivent donc des pages dans toute la structure. PostgreSQL stocke les lignes dans un heap avec des index séparés, ce qui le rend moins sensible au caractère aléatoire des clés, mais ses index perdent tout de même en localité de cache avec des clés v4 aléatoires. L’ampleur varie selon le moteur, la charge de travail et le matériel : traitez donc avec scepticisme les pourcentages précis issus de benchmarks de blogs non sourcés, et mesurez sur votre propre table. Le mécanisme lui-même, en revanche, ne fait pas débat.

L’ordonnancement temporel tient également lors de rafales d’insertions. Les implémentations ajoutent un compteur infra-milliseconde afin que les identifiants générés dans la même milliseconde restent correctement triés : uuid.uuid7() en Python réserve 42 bits en guise de compteur pour que les valeurs créées au cours d’une même milliseconde conservent leur ordre, et uuidv7() dans PostgreSQL 18 construit chaque valeur à partir d’un horodatage Unix en millisecondes, d’une fraction infra-milliseconde et de bits aléatoires.

Quand UUID v4 reste le bon choix

Choisissez v4 lorsque l’identifiant est exposé et que sa date de création est sensible, car un identifiant v7 intègre sa propre date de création à la milliseconde près. Quiconque peut lire l’identifiant peut lire la date de création de l’enregistrement, ce qui rend v7 peu adapté aux identifiants exposés publiquement. L’équipe d’Aiven parvient à la même conclusion : dès lors qu’une clé primaire est transmise aux utilisateurs finaux via une application ou une API externe, v7 cesse d’être un choix judicieux, puisque l’identifiant révèle la date de création de l’enregistrement. Utilisez v4 pour les jetons d’invitation, les liens de partage, ou partout où un concurrent pourrait déduire votre taux de croissance des plages d’identifiants.

Une réserve s’applique aux deux versions : aucun UUID n’est un jeton de sécurité. Les bits non aléatoires de v7 sont prévisibles, et la qualité de l’aléa de v4 dépend de l’implémentation : ni l’un ni l’autre ne doit conditionner l’authentification. Générez une chaîne dédiée, cryptographiquement aléatoire, d’au moins 256 bits, pour vos secrets, et réservez les UUID à l’identité.

Générer des v7 aujourd’hui, et migrer progressivement

La prise en charge de v7 est désormais large, tant côté bases de données que côté langages, même si la version exacte compte :

PlateformeGénération de v7Version
PostgreSQLuuidv7() (natif)PostgreSQL 18
Pythonuuid.uuid7() (bibliothèque standard)Python 3.14
JavaScript / Nodev7() depuis uuiduuid v14.x

PostgreSQL 18 a livré uuidv7() en tant que fonction native et ajouté un alias uuidv4() pour la fonction existante gen_random_uuid() ; sur PostgreSQL 17 et versions antérieures, il vous faut une extension ou une bibliothèque côté application. Python a ajouté uuid.uuid7() à sa bibliothèque standard en 3.14, et non en 3.12, où l’appel lève une AttributeError. En JavaScript, le paquet uuid expose v7 via un import nommé ESM et, d’après son changelog, le paquet a abandonné la prise en charge de CommonJS en v12 :

// Node.js, uuid v14.x
import { v7 as uuidv7 } from "uuid";
const id = uuidv7();
-- PostgreSQL 18
SELECT uuidv7();

Si vous souhaitez constater la différence avant de vous engager sur un type de colonne, générez un lot de chaque et comparez-les. Le générateur d’UUID d’OpenReplay produit des valeurs v4 ou v7 directement dans le navigateur, jusqu’à 500 à la fois, avec des options pour les majuscules, la suppression des tirets et la mise entre guillemets, afin que vous puissiez les coller directement dans du SQL ou une fixture JSON. Triez une colonne de valeurs v7 : elles ressortent dans l’ordre de création ; faites de même avec des v4 et elles se dispersent. Les valeurs proviennent de la Web Crypto API de votre propre onglet : aucune n’est envoyée où que ce soit.

La migration ne nécessite aucune réécriture. v4 et v7 partagent le même type de colonne uuid de 16 octets : vous pouvez donc conserver vos lignes v4 existantes et générer les nouvelles lignes en v7 dans la même table. La version est encodée dans la valeur, et aucun backfill n’est requis. Les nouvelles insertions se regroupent en fin d’index, et la fragmentation se résorbe progressivement à mesure que les pages sont réécrites ; il s’agit d’un effet progressif, non d’une défragmentation instantanée.

Le verdict : lequel utiliser ?

Adoptez UUID v7 par défaut pour les nouvelles clés primaires de base de données, les journaux et les flux d’événements ; choisissez v4 lorsque l’imprévisibilité est essentielle. v7 vous offre les performances d’écriture d’une clé séquentielle, avec la sécurité face aux collisions et la génération distribuée d’un UUID, et il bénéficie désormais d’une prise en charge native dans les plateformes que la plupart des équipes exploitent déjà. Réservez v4 aux identifiants publics dont la date de création doit rester confidentielle.

Deux alternatives complètent le tableau. ULID encode la même idée d’horodatage plus aléa en base32 Crockford, ce qui donne une chaîne plus courte de 26 caractères, compatible avec les URL, mais ce n’est pas une norme IETF et il n’existe pas de type de colonne uuid natif correspondant. Le simple bigint auto-incrémenté reste l’option la plus compacte et la plus rapide pour un petit système mononœud qui n’aura jamais besoin de génération d’identifiants distribuée.

Si vous mettez en place une nouvelle table aujourd’hui et que vous souffrez de problèmes d’index liés aux clés v4, basculez les nouvelles insertions vers v7, laissez vos anciennes lignes en place, et laissez l’index se stabiliser. Le gain est accessible pour un coût de migration quasi nul.

FAQ

Peut-on extraire l'horodatage de création d'une valeur UUID v7 ?

Oui. Comme l'UUID v7 stocke un horodatage Unix en millisecondes sur 48 bits dans ses premiers bits, vous pouvez décoder le moment où la valeur a été générée. PostgreSQL 18 expose uuid_extract_timestamp() précisément à cette fin, fonction qui a été étendue pour prendre en charge les valeurs de version 7. C'est un atout pour le débogage et les requêtes par plage temporelle, mais c'est aussi la raison pour laquelle v7 divulgue la date de création et ne devrait pas être utilisé pour des identifiants publics lorsque cette information temporelle est sensible.

Les UUID v7 et v4 présentent-ils le même risque de collision ?

Non, mais la différence est négligeable en pratique. L'UUID v4 comporte 122 bits aléatoires, tandis que v7 en conserve environ 74 après avoir réservé 48 bits à l'horodatage, plus les marqueurs de version et de variante. v7 dispose donc de moins de bits aléatoires, mais les collisions ne sont possibles qu'entre des identifiants générés au cours de la même milliseconde, et les implémentations ajoutent un compteur monotone à l'intérieur de cette fenêtre. Pour des charges de travail réelles, les deux sont effectivement exempts de collision à tout débit de génération réaliste.

UUID v7 améliore-t-il les performances des requêtes de lecture, ou seulement les insertions ?

v7 favorise principalement les performances en écriture et les balayages de plage, en ordonnant les lignes proches les unes des autres dans l'index, ce qui réduit les divisions de pages et améliore la localité de cache. N'attribuez pas à v7 seul de forts gains en lecture. L'amélioration officielle « jusqu'à 3x » en lecture de PostgreSQL 18 provient de son nouveau sous-système d'E/S asynchrones, une fonctionnalité indépendante d'uuidv7(). Le périmètre exact du bénéfice de performance de v7, c'est la localité d'index et le débit d'insertion.

Dois-je migrer mes clés primaires UUID v4 existantes vers v7 ?

En général, aucune migration complète n'est nécessaire. v4 et v7 partagent le même type de colonne uuid de 16 octets et la version est encodée dans la valeur : vous pouvez donc laisser vos lignes v4 existantes intactes et générer les nouvelles lignes en v7 dans la même table, sans backfill. Les nouvelles insertions se regroupent en fin d'index et la fragmentation se résorbe progressivement à mesure que les pages sont réécrites. Une réécriture des anciennes lignes ne se justifie que si la fragmentation cause déjà une gêne mesurée.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.