Zusammenfassung von UUID v4 und v7
Sowohl UUID v4 als auch v7 sind 128-Bit-Kennungen, formatiert als 32 hexadezimale Zeichen, gruppiert als 8-4-4-4-12. Der Unterschied liegt im Inhalt dieser Bits.
Eine UUID v4 sieht so aus:
f47ac10b-58cc-4372-a567-0e02b2c3d479
^^^^
Versionsnibble = 4
Eine UUID v7 sieht so aus:
018e2b3c-d4a1-7f2e-b8c9-1234567890ab
^^^^^^^^^^^^^
48 Bits Zeitstempel ms
^
Versionsnibble = 7
Dieser strukturelle Unterschied hat reale Leistungsauswirkungen für Datenbanken.
UUID v4: Struktur
UUID v4 reserviert 122 Bits für Zufälligkeit. Die restlichen 6 Bits sind reserviert:
- 4 Bits kodieren die Version (
0100= 4) - 2 Bits kodieren die Variante (
10für RFC 4122)
xxxxxxxx-xxxx-4xxx-[89ab]xxx-xxxxxxxxxxxx
Die x-Zeichen sind zufällig. Das 13. Zeichen ist immer 4. Das 17. Zeichen ist immer eines von 8, 9, a oder b.
Die Erzeugung ist in den meisten Sprachen ein einziger Aufruf:
import uuid
str(uuid.uuid4())
# 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
crypto.randomUUID() // nativ seit Node 14.17 / alle modernen Browser
# 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
import "github.com/google/uuid"
uuid.New().String()
UUID v7: Struktur
UUID v7, standardisiert in RFC 9562 (Mai 2024), verwendet die ersten 48 Bits für einen Unix-Zeitstempel in Millisekunden, gefolgt von Versionsbits und zufälligen Daten:
[48 Bits Zeitstempel ms][4 Bits Version = 7][12 Bits zufällig][2 Bits Variante][62 Bits zufällig]
Das 48-Bit-Zeitstempel-Präfix bedeutet, dass UUIDs, die in derselben Millisekunde erzeugt werden, in ihren höchstwertigen Bits nahezu identisch sind, und später erzeugte UUIDs sind lexikografisch größer. Dies ist die Eigenschaft, die v7 B-Tree-freundlich macht.
# Python 3.11+ hat uuid7 nativ in der Standardbibliothek
import uuid
str(uuid.uuid7()) # Python 3.11+
// Noch keine native Unterstützung, verwende eine Bibliothek
import { v7 as uuidv7 } from 'uuid';
uuidv7()
// '018e2b3c-d4a1-7f2e-b8c9-1234567890ab'
// Java hat kein natives UUID v7, verwende java-uuid-generator
import com.fasterxml.uuid.Generators;
Generators.timeBasedEpochRandomGenerator().generate().toString();
Warum zufällige UUIDs die Datenbankleistung beeinträchtigen
B-Tree-Indizes (verwendet von PostgreSQL, MySQL, SQL Server und den meisten anderen) halten eine sequenzielle Ordnung aufrecht. Wenn du eine neue Zeile einfügst, findet die Datenbank, wo der neue Schlüssel in den sortierten Index gehört, und platziert ihn dort.
Bei UUID v4 sind neue Schlüssel gleichmäßig zufällig über den gesamten 128-Bit-Raum verteilt. Der neue Schlüssel gehört fast nie ans Ende des Index. Die Datenbank muss:
- Die Indexseite lesen, zu der der Schlüssel gehört
- Wenn die Seite voll ist, teile sie auf (eine neue Seite zuweisen und Einträge neu verteilen)
- Übergeordnete Indexknoten aktualisieren
Bei hohen Einfügeraten verursacht dies ständige Seitenteilungen, hohe Schreibverstärkung und geringe Cache-Auslastung. Indexseiten werden aus dem Buffer Pool entfernt, sobald sie geschrieben wurden, weil neue Einfügungen fast nie dieselbe Seite zweimal treffen.
Bei UUID v7 sind neue Schlüssel immer größer als alle vorherigen Schlüssel (innerhalb derselben Millisekunde). Die Einfügungen gehen zur Blattseite ganz rechts im B-Tree. Die Seiten werden sequenziell gefüllt, Teilungen sind selten und die aktiven Seiten bleiben im Buffer Pool.
Der Unterschied ist messbar. Benchmarks in PostgreSQL mit UUID-v4- vs. v7-Primärschlüsseln zeigen typischerweise 20 bis 50% besseren Einfügungsdurchsatz für v7 bei großen Tabellen, bei deutlich geringerem Index-Bloat.
Wann welche Version verwenden
Verwende UUID v4, wenn:
- Du Sicherheitstokens, API-Schlüssel, Session-IDs oder Passwort-Zurücksetzungs-Tokens erzeugst, bei denen Unvorhersagbarkeit wichtig ist. Die vollen 122 Bits Zufälligkeit bedeuten, dass die ID von einem Angreifer nicht erraten oder sequenziert werden kann.
- Die IDs nicht als Datenbank-Primärschlüssel verwendet werden oder die Tabelle klein genug ist, dass die Indexleistung keine Rolle spielt.
- Du breite Kompatibilität benötigst. v4 ist seit 20 Jahren in allen wichtigen Sprachen und Bibliotheken verfügbar.
Verwende UUID v7, wenn:
- Du UUIDs als Datenbank-Primärschlüssel verwendest, besonders bei Tabellen mit hohen Einfügeraten.
- Du sortierbare, zeitlich geordnete IDs benötigst, die ohne eine separate Zeitstempelspalte chronologisch verglichen werden können.
- Du ein verteiltes System entwirfst und den Koordinationsaufwand reduzieren möchtest. Das Zeitstempel-Präfix bietet eine natürliche Ordnung ohne zentralen Sequenzgenerator.
- Du ein Event-Log, Audit-Trail oder ein beliebiges System baust, bei dem die Einfügereihenfolge wichtig ist.
UUID v7 vs ULID
ULID (Universally Unique Lexicographically Sortable Identifier) löst dasselbe Problem wie UUID v7 (sortierbare, zeitlich geordnete IDs) mit einem anderen Format. ULIDs verwenden 48-Bit-Zeitstempel in Millisekunden und 80 Bits Zufälligkeit, kodiert als 26 Crockford-Base32-Zeichen (z.B. 01ARZ3NDEKTSV4RRFFQ69G5FAV).
Der praktische Unterschied ist die Formatkompatibilität. Wenn dein System UUID-formatierte Strings erwartet (36 Zeichen, hex, mit Bindestrichen), verwende UUID v7. Wenn du kürzere, URL-sichere Kennungen möchtest und das Format durchgängig kontrollierst, ist ULID eine vernünftige Wahl. Beide sind nach Erzeugungszeit geordnet; beide vermeiden das B-Tree-Problem.
UUID v7 hat den Vorteil, ein IETF-Standard (RFC 9562) zu sein, was bedeutet, dass die Ökosystemunterstützung schnell wächst.
UUID-Versionen in der Praxis identifizieren
Bei einem gegebenen UUID-String steht die Version immer an Position 14 (0-indexiert), das erste Zeichen der dritten Gruppe:
xxxxxxxx-xxxx-Vxxx-xxxx-xxxxxxxxxxxx
^
Position 14, immer die Versionsziffer
1-> v1 (zeitbasiert, MAC-Adresse)3-> v3 (namenbasiert, MD5)4-> v4 (zufällig)5-> v5 (namenbasiert, SHA-1)7-> v7 (zeitlich geordnet, zufällig)
Die Variante steht an Position 19 (das erste Zeichen der vierten Gruppe): 8, 9, a oder b für RFC-4122-UUIDs. Wenn du eine 0 oder f an Position 14 siehst, ist der Wert wahrscheinlich keine Standard-UUID. Es könnte eine Null-UUID, eine Max-UUID oder ein nicht-standardmäßiger Bezeichner im UUID-Format sein.