UUID ou GUID : ce qui diffère vraiment

GUID est le nom que Microsoft donne aux UUID. Les différences rencontrées en pratique tiennent à la mise en forme du texte, à l'ordre des octets quand la valeur est stockée sur 16 octets et à l'ordre de tri de SQL Server.

Si vous voulez simplement la réponse courte : un GUID et un UUID sont le même type de valeur, un identifiant de 128 bits généralement écrit sous forme de 32 chiffres hexadécimaux répartis en cinq groupes. Guid.NewGuid() en .NET et crypto.randomUUID() en JavaScript produisent tous deux des UUID aléatoires de version 4. Des problèmes d'interopérabilité surviennent pourtant, et ils ont trois origines : la façon dont la valeur est écrite en texte, l'ordre de ses 16 octets et la manière dont SQL Server la compare.

Deux noms pour un seul standard

Les UUID sont issus du Network Computing System d'Apollo et ont été repris dans l'OSF Distributed Computing Environment (DCE). Microsoft a adopté la conception du DCE pour COM et a baptisé ces valeurs globally unique identifiers, d'où le sigle GUID. L'IETF a ensuite publié le format dans la RFC 4122 en juillet 2005, remplacée en mai 2024 par la RFC 9562, la version qui a ajouté les UUID v6, v7 et v8. Les deux RFC indiquent explicitement que les UUID sont aussi appelés GUID.

Un champ garde la trace de cette histoire. Les bits de variante, au début du quatrième groupe, distinguent les UUID RFC, dont ce groupe commence par 8, 9, a ou b, d'une plage réservée à la rétrocompatibilité Microsoft, qui commence par c ou d. Les GUID créés par les versions actuelles de Windows et de .NET utilisent la variante RFC : en pratique, impossible de distinguer les uns des autres.

Les formats texte

En .NET, Guid.ToString() accepte un spécificateur de format. Voici les sorties obtenues pour une même valeur avec .NET 8 :

SpécificateurSortie
Nf47ac10b58cc4372a5670e02b2c3d479
D (par défaut)f47ac10b-58cc-4372-a567-0e02b2c3d479
B{f47ac10b-58cc-4372-a567-0e02b2c3d479}
P(f47ac10b-58cc-4372-a567-0e02b2c3d479)
X{0xf47ac10b,0x58cc,0x4372,{0xa5,0x67,0x0e,0x02,0xb2,0xc3,0xd4,0x79}}

Toutes sont en minuscules. Le style en majuscules avec accolades, {F47AC10B-58CC-4372-A567-0E02B2C3D479}, vient du Registre Windows, des identifiants de classe COM et des fichiers de projet Visual Studio, et SQL Server affiche lui aussi les valeurs uniqueidentifier en majuscules. La RFC 4122 demande une sortie en minuscules et une lecture insensible à la casse, et Guid.Parse accepte toutes les formes du tableau.

La règle pratique : ne comparez jamais des GUID comme des chaînes. "{F47AC10B-...}" et "f47ac10b-..." sont deux chaînes différentes pour une même valeur. Analysez les deux côtés avec un parseur, ou normalisez en minuscules sans accolades avant de stocker ou de comparer.

L'ordre des octets : la différence qui fait mal

Stocké sur 16 octets, un UUID RFC est en big-endian : les octets se suivent dans le même ordre que les chiffres hexadécimaux. La structure GUID de Microsoft est différente. Elle est déclarée comme un entier de 32 bits, deux entiers de 16 bits et 8 octets isolés, et sur les machines little-endian, les octets des trois entiers sont stockés à l'envers. Guid.ToByteArray() renvoie exactement cette disposition, et new Guid(byte[]) l'attend :

Text:           f47ac10b-58cc-4372-a567-0e02b2c3d479
RFC bytes:      f4 7a c1 0b  58 cc  43 72  a5 67  0e 02 b2 c3 d4 79
ToByteArray():  0b c1 7a f4  cc 58  72 43  a5 67  0e 02 b2 c3 d4 79

Les 8 derniers octets sont identiques ; les trois premiers groupes sont inversés. SQL Server utilise la même disposition en endianness mixte : convertir un uniqueidentifier en binary(16) donne les octets de ToByteArray().

Le bug apparaît quand un côté écrit les octets dans une disposition et que l'autre les lit dans l'autre. Un service .NET écrit ToByteArray() dans une colonne BINARY(16) ou dans un message binaire ; un service Java, Go ou Python lit ces octets comme un UUID RFC et obtient 0bc17af4-cc58-7243-a567-0e02b2c3d479. La valeur reste unique, donc rien n'échoue, mais les recherches entre les deux systèmes ne trouvent jamais de correspondance. Le chiffre de version a bougé lui aussi : dans cet exemple, la valeur brouillée prétend être de version 7.

Choisissez une disposition et convertissez explicitement à la frontière entre les systèmes :

  • .NET 8 et versions ultérieures : guid.ToByteArray(bigEndian: true) et new Guid(bytes, bigEndian: true) lisent et écrivent dans l'ordre RFC.
  • Python : uuid.UUID(bytes_le=data) lit la disposition Microsoft, et u.bytes_le l'écrit.
  • Partout : si vous stockez du texte ou un type UUID natif plutôt que des octets bruts, la question ne se pose jamais.

Comment SQL Server trie les uniqueidentifier

SQL Server ne trie pas les GUID dans l'ordre où se lit leur texte. Il compare d'abord le dernier groupe de six octets, puis le quatrième groupe, puis les troisième, deuxième et premier groupes, et à l'intérieur de ces trois premiers groupes, c'est l'octet le plus à droite qui compte le plus. Cette liste est triée dans l'ordre croissant des uniqueidentifier, tel que le produit le type SqlGuid de .NET, qui implémente la même comparaison :

01000000-0000-0000-0000-000000000000
ff000000-0000-0000-0000-000000000000
00000001-0000-0000-0000-000000000000
00000000-0100-0000-0000-000000000000
00000000-0001-0000-0000-000000000000
00000000-0000-0100-0000-000000000000
00000000-0000-0001-0000-000000000000
00000000-0000-0000-0001-000000000000
00000000-0000-0000-0100-000000000000
00000000-0000-0000-0000-000000000002
00000000-0000-0000-0000-010000000000

Trier les mêmes valeurs en tant que System.Guid dans .NET, ou en tant que chaînes dans n'importe quel langage, donne un ordre différent : 00000000-0000-0000-0000-000000000002 arrive en premier et ff000000-0000-0000-0000-000000000000 en dernier. Deux conséquences en découlent :

  • La pagination par clé (keyset) peut sauter ou répéter des lignes si l'application trie elle-même les GUID puis demande à SQL Server les lignes WHERE id > @last. Laissez la base de données se charger des deux.
  • UUID v7 n'aide pas dans SQL Server. Une valeur v7 commence par un timestamp, mais SQL Server compare d'abord la fin aléatoire : les clés v7, y compris celles de Guid.CreateVersion7() dans .NET 9, arrivent à des positions aléatoires dans un index uniqueidentifier, exactement comme les clés v4.

NEWID vs NEWSEQUENTIALID

NEWID()NEWSEQUENTIALID()
GénérationAléatoire, version 4UuidCreateSequential de Windows, un UUID basé sur le temps, dont les octets sont réarrangés
Où l'utiliserPartout en T-SQLUniquement dans une contrainte DEFAULT sur une colonne uniqueidentifier
OrdreAléatoireCroissant selon l'ordre de tri de SQL Server sur cette machine ; peut repartir plus bas après un redémarrage de Windows
PrévisibleNonOui : la valeur suivante peut être devinée
Comportement de l'indexLes insertions arrivent n'importe où et provoquent des divisions de pagesLes insertions vont à la fin de l'index

NEWSEQUENTIALID() existe parce que les clés aléatoires fragmentent les index cluster. C'est le réarrangement des octets qui rend ses valeurs croissantes selon la comparaison inhabituelle de SQL Server, et c'est aussi pourquoi les trier comme du texte ne reproduit pas leur ordre de création. Comme les valeurs peuvent être devinées, la documentation de Microsoft le déconseille lorsque la confidentialité compte : gardez ces ID hors des URL. Si les clés sont plutôt générées dans l'application, reproduire le tri de SQL Server impose de placer les octets temporels dans le dernier groupe, soit l'inverse de ce que fait UUID v7.

Générer l'un ou l'autre

Puisque les valeurs sont les mêmes, tout générateur d'UUID v4 est un générateur de GUID. Le générateur de GUID produit des valeurs aléatoires de version 4 dans le style du Registre, en majuscules avec accolades, et le générateur d'UUID v4 produit le même type de valeur sous la forme RFC en minuscules. Tous deux permettent d'activer ou de retirer les accolades, les majuscules et les tirets, ce qui couvre les formats N, D et B ci-dessus.

Questions Fréquentes

Un GUID est-il la même chose qu'un UUID ?

Oui. GUID est le nom que Microsoft utilise pour le format UUID. Les deux sont des valeurs de 128 bits, et Guid.NewGuid() produit un UUID aléatoire standard de version 4.

Pourquoi mon GUID a-t-il changé d'aspect après sa lecture en base de données ?

Les octets des trois premiers groupes sont inversés. ToByteArray() de .NET et SQL Server stockent ces groupes en little-endian, alors que la plupart des autres langages lisent 16 octets dans l'ordre big-endian de la RFC. Convertissez explicitement, ou stockez la valeur sous forme de texte ou dans un type UUID natif.

Les GUID doivent-ils être en majuscules ou en minuscules ?

Les deux sont valides. La RFC 4122 recommande une sortie en minuscules et une lecture insensible à la casse ; les outils Windows et SQL Server affichent des majuscules. Comparez des valeurs analysées plutôt que des chaînes.

UUID v7 garde-t-il les index SQL Server dans l'ordre ?

Non. SQL Server compare d'abord les six derniers octets d'un uniqueidentifier, et dans une valeur v7 ces octets sont aléatoires. NEWSEQUENTIALID() est ordonné pour SQL Server ; v7 est ordonné pour les bases de données qui comparent les octets de gauche à droite.