Was ist eine UUID?
Eine UUID (Universally Unique Identifier, universell eindeutiger Bezeichner), im Microsoft-Sprachgebrauch auch GUID (Globally Unique Identifier) genannt, ist eine 128-Bit-Zahl, die zur eindeutigen Identifikation von Informationen in Computersystemen verwendet wird. Im Gegensatz zu automatisch inkrementierenden Ganzzahlen, die eine zentrale Stelle zur Vergabe des nächsten Wertes benötigen, können UUIDs von jeder Maschine, zu jedem Zeitpunkt und unabhängig generiert werden — mit einer astronomisch geringen Kollisionswahrscheinlichkeit.
Die kanonische Textdarstellung einer UUID sieht folgendermaßen aus:
550e8400-e29b-41d4-a716-446655440000
^^^^^^^^ ^^^^ ^^^^ ^^^^ ^^^^^^^^^^^^
Zeit Zeit Ver. Var. Knoten
Diese durch Bindestriche getrennte Zeichenkette kodiert 32 hexadezimale Ziffern (128 Bit) im Format xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx, wobei:
- M das Versionsbit ist (1, 3, 4, 5 oder 7)
- N das Variantenbit ist (8, 9, a oder b für RFC 4122-konforme UUIDs)
Eine kurze Geschichte
Das Konzept des universell eindeutigen Identifikators entstand Ende der 1980er Jahre bei Apollo Computer und der Digital Equipment Corporation (DEC) als Teil des Network Computing System (NCS) und der Distributed Computing Environment (DCE). Das Ziel war es, verteilten Systemen die Erstellung von Bezeichnern ohne zentrale Registrierung zu ermöglichen.
Microsoft übernahm das Konzept für COM/OLE und nannte es GUID, aber die zugrunde liegende Struktur ist identisch mit einer UUID. Die beiden Begriffe sind in der Praxis austauschbar.
Die formale Spezifikation folgte mit RFC 4122, veröffentlicht im Juli 2005 durch die IETF. Sie standardisierte fünf UUID-Versionen (v1–v5) und definierte die Variantenbits. Zwei Jahrzehnte später ersetzte RFC 9562 (veröffentlicht im Mai 2024) RFC 4122 und fügte offiziell UUID v6 und v7 hinzu, die die Datenbankperformance-Schwächen früherer Versionen beheben.
UUID-Struktur: 128 Bit erklärt
Eine UUID besteht immer aus genau 128 Bit (16 Byte). Als Zeichenkette formatiert hat sie 36 Zeichen (32 hexadezimale Ziffern + 4 Bindestriche).
Feld Bit Hex-Ziffern Beschreibung
──────────────────────────────────────────────────────────────
time_low 32 8 Untere 32 Bit des Zeitstempels (v1)
time_mid 16 4 Mittlere 16 Bit des Zeitstempels (v1)
time_hi_and_version 16 4 Obere 12 Bit des Zeitstempels + 4-Bit-Version
clock_seq_hi_res 8 2 Variantenbits + hohe Taktfolge
clock_seq_low 8 2 Niedrige Taktfolge
node 48 12 Knoten-ID (MAC-Adresse in v1, sonst zufällig)
Die Version ist in den höchstwertigen 4 Bit des Feldes time_hi_and_version (das 13. Hex-Zeichen) kodiert. Die Variante ist in den höchstwertigen 2–3 Bit von clock_seq_hi_res (das 17. Hex-Zeichen) kodiert.
UUID-Versionen im Überblick
Version 1 — Zeitbasiert
UUID v1 kombiniert einen 60-Bit-Zeitstempel (100-Nanosekunden-Intervalle seit dem 15. Oktober 1582) mit der MAC-Adresse des Hosts und einer Taktfolge.
Beispiel: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
Vorteile: Enthält zeitliche Informationen, was eine ungefähre Sortierung nach Erstellungszeitpunkt ermöglicht.
Nachteile: Enthält die MAC-Adresse, was Datenschutzprobleme aufwirft — es kann enthüllen, wann und wo eine UUID generiert wurde. Nicht empfohlen für öffentlich zugängliche Bezeichner.
Version 2 — DCE-Sicherheit
UUID v2 ersetzt einen Teil des Zeitstempels durch eine POSIX-UID/GID und einen Domänenbezeichner. Sie ist in der DCE 1.1-Spezifikation definiert, wird aber in der Praxis kaum verwendet, da sie Eindeutigkeitsgarantien für die Domäneneinbettung opfert.
Version 3 — Namensbasiert (MD5)
UUID v3 generiert eine deterministische UUID aus einer Namensraum-UUID und einem Namen, indem diese mit MD5 gehasht werden.
Beispiel: 5df41881-3aed-3515-88a7-2f4a814cf09e (Namensraum DNS + "example.com")
Anwendungsfall: Stabile, reproduzierbare Bezeichner für dieselbe logische Ressource. Wenn derselbe Namensraum und Name zweimal gehasht werden, erhält man immer dieselbe UUID.
Vorsicht: MD5 ist kryptografisch schwach. Für neue Systeme wird v5 bevorzugt.
Version 4 — Zufällig (Am beliebtesten)
UUID v4 verwendet 122 Bit kryptografisch zufällige Daten (die verbleibenden 6 Bit werden für Version und Variante verwendet).
Beispiel: 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d
Dies ist bei weitem die am häufigsten verwendete UUID-Version. Sie erfordert keine Koordination, keinen Netzwerkzugriff und kein Wissen über den Host. Die Zufälligkeit stammt von einem CSPRNG (kryptografisch sicherem Pseudozufallszahlengenerator).
Version 5 — Namensbasiert (SHA-1)
UUID v5 ist konzeptionell identisch mit v3, verwendet aber SHA-1 anstelle von MD5.
Beispiel: 886313e1-3b8a-5372-9b90-0c9aee199e5d (Namensraum DNS + "example.com")
Anwendungsfall: Wenn deterministische, reproduzierbare UUIDs benötigt werden und ein stärkerer Hash als MD5 gewünscht wird. SHA-1 ist nicht für alle Zwecke kryptografisch sicher, hat aber eine deutlich bessere Kollisionsresistenz als MD5.
Version 7 — Zeitgeordnet (Empfohlen für Datenbanken)
UUID v7 ist der Star von RFC 9562. Sie bettet einen 48-Bit-Unix-Zeitstempel in Millisekunden in die höchstwertigen Bits ein, wodurch die UUID k-sortierbar wird: Später generierte UUIDs werden nach früher generierten UUIDs einsortiert.
Beispiel: 018e8f5a-2b3c-7d4e-8f9a-0b1c2d3e4f50
Die ersten 12 Hex-Zeichen (018e8f5a-2b3c) kodieren den Millisekunden-Zeitstempel. Der Rest ist zufällig. Dies macht v7 ideal als Datenbankprimärschlüssel: Sie behält die globale Eindeutigkeit von v4 bei und ist gleichzeitig einfügereihenfolge-geordnet, was die B-Baum-Indexfragmentierung drastisch reduziert.
Kollisionswahrscheinlichkeit: Das Geburtstagsproblem
UUID v4 verwendet 122 Bit Zufälligkeit. Die Kollisionswahrscheinlichkeit lässt sich mit der Geburtstagsproblem-Formel abschätzen:
p(n) ≈ 1 − e^(−n²/2N)
wobei N = 2^122 ≈ 5,32 × 10^36 (Gesamtzahl möglicher UUIDs).
Um eine 50%ige Wahrscheinlichkeit mindestens einer Kollision zu haben, müsste man ungefähr 2,71 × 10^18 (2,71 Trillionen) UUIDs generieren. Bei einer Rate von 1 Milliarde UUIDs pro Sekunde würde das etwa 86 Jahre dauern.
Für praktische Zwecke sind UUID v4-Kollisionen so unwahrscheinlich, dass sie in jeder realen Anwendung als unmöglich behandelt werden können.
UUID vs. Auto-Increment-IDs
| Merkmal | UUID v4 | Auto-Increment |
|---|---|---|
| Eindeutigkeitsbereich | Global | Lokal (pro Tabelle) |
| Generierungsort | Client-seitig | Server-seitig |
| Vorhersagbarkeit | Nicht vorhersagbar | Sequentiell |
| URL-Sicherheit | Ja (mit Kodierung) | Ja |
| DB-Indexperformance | Schlecht (zufällig) | Ausgezeichnet (sequentiell) |
| Zusammenführen/Replikation | Einfach | Konfliktanfällig |
| Speichergröße | 16 Byte (binär) | 4–8 Byte |
| Menschliche Lesbarkeit | Gering | Hoch |
| Sicherheit (Enumeration) | Sicher | Anfällig |
Auto-Increment-IDs sind für Single-Datenbank-Anwendungen ohne verteilte Anforderungen vollkommen geeignet. UUIDs glänzen, wenn:
- Mehrere Dienste oder Datenbanken IDs unabhängig generieren müssen.
- Datensätze aus verschiedenen Quellen zusammengeführt werden können.
- Sequentielle IDs in URLs nicht exponiert werden sollen (Enumerationsangriffe).
Datenbankperformance: v4 vs. v7
Das Problem mit UUID v4 in Datenbanken
Da UUID v4 zufällig ist, wird jede neue Zeile an einer zufälligen Position im B-Baum-Index eingefügt. Dies führt zu:
- Index-Seitenteilungen — die Datenbank muss Index-Seiten häufig aufteilen, um Einfügungen außer der Reihe aufzunehmen.
- Cache-Thrashing — zufällige Zugriffsmuster machen den Pufferpool unwirksam und verursachen häufige Festplattenlesevorgänge.
- Schreibverstärkung — deutlich mehr I/O als bei sequentiellen Einfügungen.
Benchmarks auf großen Tabellen (>10 Mio. Zeilen) zeigen häufig, dass UUID v4-Primärschlüssel bei schreibintensiven Workloads 3–5-mal schlechter abschneiden als sequentielle Schlüssel.
UUID v7 löst dieses Problem
UUID v7 ist innerhalb desselben Millisekunden-Fensters monoton steigend. Neue Datensätze werden am Ende oder nahe dem Ende des Index eingefügt, ähnlich wie Auto-Increment. Das Ergebnis:
- Nahezu keine Indexfragmentierung.
- Optimale Pufferpool-Auslastung.
- Einfügungsleistung vergleichbar mit Auto-Increment.
Empfehlung: Verwende UUID v7 für jedes neue Datenbankschema, das global eindeutige Primärschlüssel benötigt.
Anwendungsfälle in Verteilten Systemen
UUIDs sind das Rückgrat der verteilten Identität:
- Microservices: Jeder Dienst kann IDs unabhängig generieren, ohne mit einem zentralen Sequenzserver zu kommunizieren.
- Event Sourcing: Ereignisse erhalten unveränderliche IDs, die über Wiedergaben und Rehydrierungen hinweg eindeutig bleiben.
- CQRS: Befehle und Abfragen können durch eine client-seitig generierte UUID korreliert werden.
- Multi-Region-Datenbanken: In verschiedenen Regionen erstellte Datensätze kollidieren nie, was eventuelle Konsistenz und Zusammenführungsoperationen trivial macht.
- Idempotenz-Schlüssel: APIs können client-generierte UUIDs als Idempotenz-Schlüssel verwenden, um Anfragen sicher zu wiederholen.
- Inhaltsadressierbare Systeme: UUID v3/v5 können stabile Bezeichner für dieselbe Ressource systemübergreifend produzieren.
ULID: Eine Moderne Alternative
ULID (Universally Unique Lexicographically Sortable Identifier) ist eine von der Community entwickelte Alternative zu UUID, die standardmäßig URL-sicher und immer sortierbar ist.
| Merkmal | UUID v4 | UUID v7 | ULID |
|---|---|---|---|
| Sortierbar | Nein | Ja | Ja |
| URL-sicher | Mit Kodierung | Mit Kodierung | Ja (Crockford Base32) |
| Zeitkomponente | Nein | Ja (ms) | Ja (ms) |
| Standard | RFC 9562 | RFC 9562 | Community-Spezifikation |
| Formatlänge | 36 Zeichen | 36 Zeichen | 26 Zeichen |
| Monoton innerhalb ms | Nein | Optional | Ja |
Ein ULID sieht so aus: 01ARZ3NDEKTSV4RRFFQ69G5FAV — 26 Zeichen, keine Bindestriche, direkt in URLs verwendbar. Wenn Sortierbarkeit und URL-Sicherheit ohne RFC-Kompatibilitätsbedenken benötigt werden, ist ULID eine Überlegung wert.
UUID-Generierung in verschiedenen Programmiersprachen
JavaScript / TypeScript (Node.js und Browser)
import { v4 as uuidv4, v7 as uuidv7 } from 'uuid';
const randomId = uuidv4(); // "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
const sortableId = uuidv7(); // "018e8f5a-2b3c-7d4e-8f9a-0b1c2d3e4f50"
// Natives crypto (Node 14.17+ / moderne Browser)
const nativeId = crypto.randomUUID(); // UUID v4
Python
import uuid
# v4 — zufällig
print(uuid.uuid4()) # "f47ac10b-58cc-4372-a567-0e02b2c3d479"
# v5 — deterministisch aus Namensraum + Name
print(uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com'))
# "886313e1-3b8a-5372-9b90-0c9aee199e5d"
Go
import "github.com/google/uuid"
id := uuid.New() // UUID v4
v7, _ := uuid.NewV7() // UUID v7 (google/uuid v1.6+)
fmt.Println(id.String())
Java
import java.util.UUID;
UUID v4 = UUID.randomUUID();
System.out.println(v4); // "3e4666bf-d5e5-4aa7-b8ce-cefe41c7568a"
Rust
use uuid::Uuid;
let v4 = Uuid::new_v4();
let v7 = Uuid::now_v7();
println!("{}", v4);
println!("{}", v7);
Best Practices
- Verwende v4 für allgemeine eindeutige Bezeichner, wo Reihenfolge keine Rolle spielt.
- Verwende v7 für Datenbankprimärschlüssel, um Indexfragmentierung zu vermeiden und natürliche Sortierreihenfolge zu erhalten.
- Verwende v5 für deterministische IDs, wenn dieselbe Eingabe immer dieselbe UUID erzeugen soll (z. B. Deduplizierung von Inhalten nach URL).
- Vermeide v1 in datenschutzsensiblen Kontexten — sie enthält deine MAC-Adresse und Erstellungszeit.
- Speichere UUIDs als Binär (16 Byte) in der Datenbank, nicht als varchar(36), um Speicherplatz zu sparen und die Indexleistung zu verbessern.
- Verwende v2 nie, außer du implementierst speziell DCE Security.
- Validiere UUID-Eingaben an API-Grenzen, um die Injektion fehlerhafter Bezeichner zu verhindern.
- Verwende eine gut getestete Bibliothek anstatt eigene UUID-Generierung zu implementieren — Entropiequellen sind leicht falsch zu implementieren.
Häufig gestellte Fragen
F: Sind UUID und GUID dasselbe?
A: Funktional ja. GUID ist Microsofts Name für dasselbe 128-Bit-Bezeichnerformat. Die Struktur ist identisch; nur die Terminologie unterscheidet sich.
F: Können zwei UUIDs jemals gleich sein?
A: Theoretisch ja, aber die Wahrscheinlichkeit ist bei v4 vernachlässigbar klein. Man müsste 2,71 Trillionen UUIDs generieren, um eine 50%ige Kollisionswahrscheinlichkeit zu erreichen. In der Praxis wird man nie einer Kollision begegnen.
F: Welche UUID-Version sollte ich für Datenbankprimärschlüssel verwenden?
A: UUID v7 ist die beste Wahl. Sie ist zeitgeordnet (gut für B-Baum-Indizes), global eindeutig und in RFC 9562 standardisiert. Wenn deine Bibliothek v7 noch nicht unterstützt, verwende ein ULID oder ein "COMB"-UUID-Muster als Workaround.
F: Ist UUID v4 sicher genug als Session-Token?
A: UUID v4 hat 122 Bit Zufälligkeit, was im Allgemeinen ausreicht. Dedizierte Session-Token-Bibliotheken können jedoch etwas mehr Entropie oder bessere Kodierungen verwenden. Für sicherheitskritische Kontexte bevorzuge einen dedizierten Token-Generator.
F: Wie speichere ich eine UUID effizient in einer SQL-Datenbank?
A: Verwende einen nativen UUID-Spaltentyp, falls verfügbar (PostgreSQL, MySQL 8+), oder eine BINARY(16)-Spalte. Vermeide CHAR(36) / VARCHAR(36), das 2,25-mal mehr Platz belegt und langsamer zu indizieren ist.
F: Was ist der Unterschied zwischen UUID v3 und v5?
A: Beide sind namensbasiert und deterministisch. v3 verwendet MD5; v5 verwendet SHA-1. SHA-1 hat eine bessere Kollisionsresistenz, daher wird v5 für neue Systeme bevorzugt. Keines sollte für sicherheitskritisches Hashing verwendet werden.
F: Ersetzt UUID v7 UUID v4?
A: Für Datenbankprimärschlüssel ja — v7 ist strikt besser. Für andere Anwendungsfälle (z. B. API-Schlüssel, Tokens, Korrelations-IDs), wo die Reihenfolge keine Rolle spielt, ist v4 weiterhin vollkommen geeignet.
Zusammenfassung
UUIDs sind ein grundlegendes Primitiv in der modernen Softwareentwicklung. Das Verständnis der Unterschiede zwischen den Versionen hilft dabei, die richtigen Abwägungen zu treffen:
- v4 — am besten für allgemeine Eindeutigkeit ohne Koordination.
- v5 — am besten für deterministische, reproduzierbare Bezeichner.
- v7 — am besten für Datenbankprimärschlüssel aufgrund der zeitlichen Ordnung.
Ob du einen Monolithen, eine Microservices-Architektur oder eine global verteilte Datenbank baust — UUIDs bieten einen kampferprobten, standardisierten Weg, deine Daten eindeutig und sicher zu identifizieren.