UUID vs. GUID: Was sich tatsächlich unterscheidet

GUID ist Microsofts Name für eine UUID. Die Unterschiede, die Ihnen in der Praxis begegnen, sind die Textdarstellung, die Bytereihenfolge beim Speichern als 16 Bytes und die Sortierung in SQL Server.

Direkt ausprobieren: GUID-Generator UUID-v4-Generator

Die kurze Antwort vorweg: GUID und UUID bezeichnen dieselbe Art von Wert, einen 128-Bit-Bezeichner, der meist als 32 Hex-Ziffern in fünf Gruppen geschrieben wird. Guid.NewGuid() in .NET und crypto.randomUUID() in JavaScript erzeugen beide zufällige UUIDs der Version 4. Trotzdem kommt es immer wieder zu Problemen zwischen Systemen, und die haben drei Ursachen: wie der Wert als Text geschrieben wird, in welcher Reihenfolge seine 16 Bytes stehen und wie SQL Server ihn vergleicht.

Zwei Namen für einen Standard

UUIDs stammen aus dem Network Computing System von Apollo und wurden in das Distributed Computing Environment (DCE) der OSF übernommen. Microsoft übernahm das DCE-Design für COM und nannte die Werte Globally Unique Identifiers. Die IETF veröffentlichte das Format später im Juli 2005 als RFC 4122, der im Mai 2024 durch RFC 9562 ersetzt wurde – die Fassung, die UUID v6, v7 und v8 ergänzt hat. Beide RFCs sagen ausdrücklich, dass UUIDs auch als GUIDs bekannt sind.

Ein Feld hält diese Namensgeschichte noch fest. Die Variantenbits am Anfang der vierten Gruppe unterscheiden RFC-UUIDs, bei denen diese Gruppe mit 8, 9, a oder b beginnt, von einem Bereich, der für die Abwärtskompatibilität mit Microsoft reserviert ist und mit c oder d beginnt. GUIDs, die aktuelle Windows- und .NET-Versionen erzeugen, verwenden die RFC-Variante, in der Praxis sind die beiden also nicht zu unterscheiden.

Textformate

Guid.ToString() in .NET akzeptiert einen Formatbezeichner. Das sind die Ausgaben für denselben Wert in .NET 8:

BezeichnerAusgabe
Nf47ac10b58cc4372a5670e02b2c3d479
D (Standard)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}}

Alle Ausgaben sind kleingeschrieben. Die Großschreibung mit geschweiften Klammern, {F47AC10B-58CC-4372-A567-0E02B2C3D479}, stammt aus der Windows-Registry, aus COM-Klassen-IDs und Visual-Studio-Projektdateien, und auch SQL Server zeigt uniqueidentifier-Werte in Großbuchstaben an. RFC 4122 verlangt Kleinbuchstaben bei der Ausgabe und Unabhängigkeit von der Schreibung bei der Eingabe, und Guid.Parse akzeptiert jede Form aus der Tabelle.

Die praktische Regel lautet: Vergleichen Sie GUIDs nie als Strings. "{F47AC10B-...}" und "f47ac10b-..." sind verschiedene Strings, aber derselbe Wert. Parsen Sie beide Seiten, oder normalisieren Sie vor dem Speichern oder Vergleichen auf Kleinbuchstaben ohne Klammern.

Bytereihenfolge: der Unterschied, der wehtut

Als 16 Bytes gespeichert, ist eine RFC-UUID Big-Endian: Die Bytes stehen in derselben Reihenfolge wie die Hex-Ziffern. Microsofts GUID-Struktur ist anders. Sie ist als eine 32-Bit-Ganzzahl, zwei 16-Bit-Ganzzahlen und 8 einzelne Bytes deklariert, und auf Little-Endian-Maschinen werden die drei Ganzzahlen mit umgekehrter Bytereihenfolge gespeichert. Guid.ToByteArray() liefert genau dieses Layout, und new Guid(byte[]) erwartet es:

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

Die letzten 8 Bytes sind identisch, die ersten drei Gruppen sind umgedreht. SQL Server verwendet dasselbe gemischte Layout: Wandeln Sie einen uniqueidentifier in binary(16) um, erhalten Sie die Bytes von ToByteArray().

Der Fehler entsteht, wenn eine Seite die Bytes im einen Layout schreibt und die andere sie im anderen liest. Ein .NET-Dienst schreibt ToByteArray() in eine BINARY(16)-Spalte oder eine binäre Nachricht; ein Dienst in Java, Go oder Python liest die Bytes als RFC-UUID und erhält 0bc17af4-cc58-7243-a567-0e02b2c3d479. Der Wert ist immer noch eindeutig, es schlägt also nichts fehl, aber Abfragen zwischen den beiden Systemen finden nie einen Treffer. Auch die Versionsziffer ist gewandert: In diesem Beispiel gibt sich der verdrehte Wert als Version 7 aus.

Legen Sie sich auf ein Layout fest und konvertieren Sie an der Systemgrenze ausdrücklich:

  • .NET 8 und neuer: guid.ToByteArray(bigEndian: true) und new Guid(bytes, bigEndian: true) schreiben und lesen die RFC-Reihenfolge.
  • Python: uuid.UUID(bytes_le=data) liest das Microsoft-Layout, und u.bytes_le schreibt es.
  • Überall: Wenn Sie statt roher Bytes Text oder einen nativen UUID-Typ speichern, stellt sich die Frage gar nicht erst.

Wie SQL Server uniqueidentifier sortiert

SQL Server sortiert GUIDs nicht so, wie sich ihr Text liest. Er vergleicht zuerst die letzte Gruppe aus sechs Bytes, dann die vierte Gruppe, danach die dritte, zweite und erste Gruppe, und innerhalb dieser ersten drei Gruppen zählt das Byte ganz rechts am meisten. Die folgende Liste steht in aufsteigender uniqueidentifier-Reihenfolge, erzeugt mit dem .NET-Typ SqlGuid, der denselben Vergleich implementiert:

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

Sortiert man dieselben Werte als System.Guid in .NET oder als Strings in einer beliebigen Sprache, ergibt sich eine andere Reihenfolge: 00000000-0000-0000-0000-000000000002 kommt zuerst und ff000000-0000-0000-0000-000000000000 zuletzt. Daraus folgen zwei Dinge:

  • Keyset-Paginierung kann Zeilen überspringen oder doppelt liefern, wenn die Anwendung GUIDs selbst sortiert und SQL Server dann nach Zeilen mit WHERE id > @last fragt. Überlassen Sie beides der Datenbank.
  • UUID v7 hilft in SQL Server nicht. Ein v7-Wert beginnt zwar mit einem Zeitstempel, SQL Server vergleicht aber zuerst das zufällige Ende. v7-Schlüssel, auch die aus Guid.CreateVersion7() in .NET 9, landen in einem uniqueidentifier-Index deshalb genauso an zufälligen Positionen wie v4-Schlüssel.

NEWID vs. NEWSEQUENTIALID

NEWID()NEWSEQUENTIALID()
Wie der Wert entstehtZufällig, Version 4Windows-Funktion UuidCreateSequential, eine zeitbasierte UUID mit umsortierten Bytes
Wo Sie ihn verwenden könnenÜberall in T-SQLNur in einer DEFAULT-Einschränkung für eine uniqueidentifier-Spalte
ReihenfolgeZufälligAufsteigend in der Sortierung von SQL Server auf diesem Rechner; kann nach einem Windows-Neustart niedriger neu beginnen
VorhersagbarNeinJa: Der nächste Wert lässt sich erraten
Verhalten im IndexInserts landen irgendwo und verursachen SeitenteilungenInserts landen am Ende des Index

NEWSEQUENTIALID() gibt es, weil zufällige Schlüssel gruppierte Indizes fragmentieren. Die umsortierten Bytes sorgen dafür, dass die Werte unter dem ungewöhnlichen Vergleich von SQL Server ansteigen. Genau deshalb ergibt eine Sortierung als Text auch nicht die Reihenfolge, in der sie erzeugt wurden. Weil sich die Werte erraten lassen, rät die Microsoft-Dokumentation davon ab, wo Datenschutz eine Rolle spielt; halten Sie solche IDs aus URLs heraus. Werden die Schlüssel stattdessen in der Anwendung erzeugt, müssen für eine Sortierung wie in SQL Server die zeitbasierten Bytes in die letzte Gruppe – also genau umgekehrt zu dem, was UUID v7 macht.

Beides erzeugen

Da die Werte identisch sind, ist jeder UUID-v4-Generator auch ein GUID-Generator. Der GUID-Generator erzeugt zufällige Werte der Version 4 im Registry-Stil, also in Großbuchstaben mit geschweiften Klammern, und der UUID-v4-Generator erzeugt dieselbe Art von Wert in der kleingeschriebenen RFC-Form. Bei beiden lassen sich Klammern, Groß- und Kleinschreibung und Bindestriche umschalten, womit die Formate N, D und B von oben abgedeckt sind.

Häufige Fragen

Ist eine GUID dasselbe wie eine UUID?

Ja. GUID ist der Name, den Microsoft für das UUID-Format verwendet. Beide sind 128-Bit-Werte, und Guid.NewGuid() erzeugt eine standardkonforme zufällige UUID der Version 4.

Warum sieht meine GUID nach dem Lesen aus der Datenbank anders aus?

Die Bytes der ersten drei Gruppen sind vertauscht. ToByteArray() in .NET und SQL Server speichern diese Gruppen in Little-Endian-Reihenfolge, während die meisten anderen Sprachen 16 Bytes in der Big-Endian-Reihenfolge des RFC lesen. Konvertieren Sie ausdrücklich, oder speichern Sie den Wert als Text oder in einem nativen UUID-Typ.

Schreibt man GUIDs groß oder klein?

Beides ist gültig. RFC 4122 empfiehlt Kleinbuchstaben bei der Ausgabe und Unabhängigkeit von der Schreibung bei der Eingabe; Windows-Werkzeuge und SQL Server zeigen Großbuchstaben an. Vergleichen Sie geparste Werte statt Strings.

Hält UUID v7 Indizes in SQL Server geordnet?

Nein. SQL Server vergleicht bei einem uniqueidentifier zuerst die letzten sechs Bytes, und die sind bei einem v7-Wert zufällig. NEWSEQUENTIALID() ist für SQL Server geordnet; v7 ist für Datenbanken geordnet, die Bytes von links nach rechts vergleichen.
Direkt ausprobieren: GUID-Generator UUID-v4-Generator