UUID, Universally Unique IDentifier

Un UUID, ou Universally Unique IDentifier, est un nombre entier de 128 bits, encodé en 32 chiffres hexadécimaux. Plus préciséement, le format habituellement utilisé est constitué de 8 + 3x4 + 12 chiffres: 00000000-0000-0000-0000-000000000000.

Les UUIDs étaient utilisés initialement par des systèmes d’exploitation (Microsoft Windows), middlewares (DCE) et applications () pour identifier des composants. Les RFCs citées en référence sont d’ailleurs basées sur la spécification de DCE. Ces UUID peuvent être utilisés sous des noms différents comme GUID, CLSID, OID,…​ Aujourd’hui on utilise couramment le type UUID en base de données pour les clés primaires.

Si le format de l’UUID est stable, il y a plusieurs versions pour la génération, définies dans la RFC 4122 en juillet 2005.

  • v1: basé sur une adresse MAC sur 48 bits et un horodatage (100 ns) sur 74 bits

  • v2: compatibilité avec DCE

  • v3: basé sur un nom hashé en MD5

  • v4: totalement aléatoire

  • v5: idem v3, mais en SHA1

Depuis mai 2024, avec la RFC 9562, il y en a 3 de plus.

  • v6: idem v1, mais avec un ordre plus logique

  • v7: basé sur un horodatage avec EPOCH comme base

  • v8: implémentations spécifiques

Le 1° chiffre du 3° groupe représente la version.

{nbsp} 00000000-0000-**9**000-0000-000000000000 {nbsp}
En plus de la version (4 bits), il y a une information de variante, sur 2 bits, en début de 3° groupe.

UUID v1

En version 1, l’UUID est généré en 3 parties.

La fin de l’UUID, sur 48 bits, est une composante spatiale, basée sur l’adresse MAC. Cette partie est donc constante sur une même machine, avec la même interface réseau.

Le début, sur 60 bits, est une composante temporelle, basée sur le temps passé (par tranche de 0.1 µs) depuis l’origine du calendrier grégorien (15/10/1582, 00h00 UTC). Ça ressemble au timestamp d’Unix, mais avec une base au 15 octobre 1582 au lieu du 1° janvier 1970 et avec une unité d’un dizième de microseconde au lieu de la milliseconde.

Au milieu, sur 14 bits, il y a une séquence pour éviter les confits.

Si on ajoute la version (4 bits) et (2 bits), on arrive au total de 128 bits.

1 2 3 4 5 6 7 8  -  1 2 3 4  -  V 2 3 4  -  1 2 3 4  -  1 2 3 4 5 6 7 8 9 0 1 2
time low         -  time mid -  time high-  clock seq-  node (MAC address)

Quand on génère une série de UUID v1, le début change à chaque fois, la fin de la composante temporelle reste relativement stable et la fin ne change jamais.

00000000-0000-1000-9c26-04d9f5d35a2b    // 1582-10-15T00:00:00.000
075bcd15-0000-1000-9c26-04d9f5d35a2b    // 1582-10-15T00:00:12.345
73ce2ff2-0b3a-1000-9c26-04d9f5d35a2b    // 1582-10-29T06:56:07.890
a630f34e-9b4b-11b6-9c26-04d9f5d35a2b    // 1974-01-02T19:15:01.234

UUID v4

Cette version ne se base sur aucune information externe. L’UUID est généré de façon (pseudo-)aléatoire.

1 2 3 4 5 6 7 8  -  1 2 3 4  -  V 2 3 4  -  1 2 3 4  -  1 2 3 4 5 6 7 8 9 0 1 2
random           -  random   -  random   -  random   -  random

C’est la version qui est utilisée habituellement pour les clés primaires en base de données. Quand on génère une série de UUID v4, la totalité change à chaque fois.

d1418e65-43fb-4610-9db2-dde2cddbe6d8
18c61ac0-8bf9-41bf-8fba-e13657671cb0
aff02b92-fbcf-4e69-a5d0-929df63916a3
982e7059-ffef-477e-8cf9-fea2e725ce66

UUID v3 et v5

Les versions 3 et 5 se ressemblent. Elles sont basée sur le hash d’un texte, en MD5 pour la v3 et SHA-1 pour la v5.

1 2 3 4 5 6 7 8  -  1 2 3 4  -  V 2 3 4  -  1 2 3 4  -  1 2 3 4 5 6 7 8 9 0 1 2
sha1 high        -  sha1 high-  sha1 mid -  sha1 low -  sha1 low

Il n’y a aucune variation dans l’algorithme. Si on génère plusieurs fois l’UUID avec le même texte, on aura le même résultat.

7c1b4a99-65ac-5caa-8e2d-da5963020190    // JTips
7c1b4a99-65ac-5caa-8e2d-da5963020190    // JTips
7c1b4a99-65ac-5caa-8e2d-da5963020190    // JTips
9df07c28-781c-5dd8-9472-5c5d80a2370f    // jtips

UUID v6 et v7

Ces versions sont plus récentes que les précédentes, elles ont été publiées en mai 2024, avec la RFC-9562 qui remplace la RFC-4122. Leur objectif principal est de rendre l’UUID plus facile à indexé, tout en se basant sur un timestamp, comme la version 1.

La version 6 utilise les mêmes données que la version 1, mais avec les parties hautes du timestamp au début.

1 2 3 4 5 6 7 8  -  1 2 3 4  -  V 2 3 4  -  1 2 3 4  -  1 2 3 4 5 6 7 8 9 0 1 2
time high        -  time mid -  time low -  clock seq-  node (MAC address)

Quand on génère une série de UUID v6, le début est relativement stable, la fin de la composante temporelle change beaucoup et la fin ne change jamais. La série est strictement croissante.

00000000-0000-6000-9c26-04d9f5d35a2b    // 1582-10-15T00:00:00.000
00000000-75bc-6d15-9c26-04d9f5d35a2b    // 1582-10-15T00:00:12.345
0000b3a7-3ce2-6ff2-9c26-04d9f5d35a2b    // 1582-10-29T06:56:07.890
1b69b4ba-630f-634e-9c26-04d9f5d35a2b    // 1974-01-02T19:15:01.234

La version 7 utilise un timestamp en millisecondes avec Epoch comme référence (01/01/1970, 00h00 UTC), sur 48 bits. Le reste est complété avec un nombre aléatoire sur 74 bits.

1 2 3 4 5 6 7 8  -  1 2 3 4  -  V 2 3 4  -  1 2 3 4  -  1 2 3 4 5 6 7 8 9 0 1 2
unix time        -  unix time-  random   -  random   -  random

Quand on génère une série de UUID v7, le début est relativement stable, la fin de la composante temporelle change beaucoup et la fin change à chaque fois. La série est strictement croissante.

00000000-0000-7642-8300-19ae07cdae82    // 1970-01-01T00:00:00.000
00000000-3039-798d-ba83-1cb0fd8eed69    // 1970-01-01T00:00:12.345
00004996-02d2-7beb-ab8e-3f63b12a1dd8    // 1970-01-15T06:56:07.890
011f71fb-04cb-7f5d-a1f8-94fead4bbb84    // 2009-02-13T23:31:30.123

C’est une bonne alternative à la version 4 pour les clés primaires en base de données. Sa valeur ajoutée est la possibilité de trier par date de création sans colonne supplémentaire. Et il est même possible d’extraire le timestamp de création de la clé.

Java

Le JDK a une classe java.util.UUID. Par contre, il n’a que la génération d’UUID v4.

UUID id = UUID.randomUUID()

Pour les autres versions, on peut utiliser des librairies. FasterXML, qui gère Jackson, propose java-uuid-generator pour ça.

  <dependency>
    <groupId>com.fasterxml.uuid</groupId>
    <artifactId>java-uuid-generator</artifactId>
    <version>5.0.0</version>
  </dependency>
// UUID v1
// based on preferred MAC address
TimeBasedGenerator generatorV1 = Generators.timeBasedGenerator(EthernetAddress.fromPreferredInterface());
UUID idV1 = generatorV1.generate();

// based on random multicast address
TimeBasedGenerator generatorV1Random = Generators.timeBasedGenerator();
UUID idV1Random = generatorV1Random.generate();

// UUID v3
NameBasedGenerator generatorV3 = Generators.nameBasedGenerator(null, MessageDigest.getInstance("MD5"));
UUID idV3 = generatorV3.generate("JTips");

// UUID v4
// based on SecureRandom
RandomBasedGenerator generatorV4 = Generators.randomBasedGenerator();
UUID idV4 = generatorV4.generate();

// UUID v5
RandomBasedGenerator generatorV5 = Generators.NameBasedGenerator();
UUID idV5 = generatorV5.generate("JTips");

// UUID v6
TimeBasedReorderedGenerator generatorV6 = Generators.timeBasedReorderedGenerator();
UUID idV6 = generatorV6.generate();

// UUID v7
TimeBasedEpochGenerator generatorV5 = Generators.timeBasedEpochGenerator();
UUID idV7 = generatorV7.generate( );

PostgreSQL

PostgreSQL a une fonction de génération d’UUID v4.

SELECT gen_random_uuid();

CREATE TABLE my_table (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    ...
)

L’extension uuid-ossp permet de générer des UUID en version 1, 3, 4 ou 5.

CREATE EXTENSION uuid-ossp SCHEMA public;

SELECT uuid_generate_v4();

CREATE TABLE my_table (
    id uuid DEFAULT uuid_generate_v4() NOT NULL,
    ...
)

En version 16, PostgreSQL ne supporte pas encore la versions 7. Pour ça, il faut passer par des extensions tierces, comme pg_uuidv7, ou une simple fonction comme uuid_generate_v7().

CREATE EXTENSION pg_uuidv7 SCHEMA public;

SELECT uuid_generate_v7();
SELECT uuid_v7_to_timestamptz('011f71fb-04cb-7f5d-a1f8-94fead4bbb84');
=> 2009-02-13 23:31:30.123+00

CREATE TABLE my_table (
    id uuid DEFAULT uuid_generate_v7() NOT NULL,
    ...
)

Le support d’UUID v7 est prévu pour la version 17, avec une fonction uuidv7(). Par soucis de cohérence, la fonction gen_random_uuid() a un nouvel alias uuidv4()

Par contre, il ne semble pas y avoir quoi que ce soit pour UUID v6.