UUID v4 vs v7 — Unterschiede und wann welche Version verwenden
UUID v4 ist seit über einem Jahrzehnt die Standardwahl für verteilte Primärschlüssel. Generieren Sie einen zufälligen 128-Bit-Wert, formatieren Sie ihn als acht Hex-Gruppen, fertig. Keine Koordination erforderlich, praktisch null Kollisionswahrscheinlichkeit, funktioniert überall. Warum verbreitet sich dann UUID v7 — standardisiert in RFC 9562 im Jahr 2024 — so schnell in Produktionssystemen im Jahr 2026?
Die kurze Antwort: Datenbankleistung. UUID v4 zerstört die B-Baum-Index-Lokalität. UUID v7 behebt das, während alles andere, was Entwickler an UUIDs lieben, erhalten bleibt. Dieser Artikel erklärt, was jede Version tatsächlich ist, warum der Unterschied im großen Maßstab wichtig ist, wann welche gewählt werden sollte und wie ohne Downtime migriert wird.
Müssen Sie jetzt UUIDs generieren? Probieren Sie den Toova UUID Generator, der v4 und v7 in großen Mengen unterstützt. Für andere zufällige Bezeichner deckt der Random String Generator und der Password Generator kürzere Tokens ab.
UUID v4 — Reine Zufälligkeit
UUID Version 4 verwendet einen kryptografisch sicheren Pseudozufallszahlengenerator (CSPRNG), um 122 der 128 Bits zu füllen. Die restlichen sechs Bits sind fest: Vier Bits kodieren die Version (0100) und zwei Bits kodieren die Variante (10). Das Ergebnis sieht so aus:
f47ac10b-58cc-4372-a567-0e02b2c3d479
^^^^
version = 4 (random) Die Zufälligkeit ist das Feature. Zwei unabhängige Systeme können UUIDs ohne Koordination generieren und kollidieren nie — die Kollisionswahrscheinlichkeit in einer Menge von 2,71 Billiarden v4-UUIDs beträgt ca. 50%, was bedeutet, dass das Risiko für jede praktische Anwendung vernachlässigbar ist. Sie benötigen keinen zentralisierten ID-Server, keine Datenbanksequenz und keinen verteilten Lock.
Wie v4 generiert wird
import { v4 as uuidv4 } from 'uuid';
const id = uuidv4();
// => 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
Die Bibliothek uuid (JavaScript), Pythons uuid.uuid4(), Gos google/uuid und jede große Sprachlaufzeitumgebung delegieren an den OS-CSPRNG — /dev/urandom unter Linux, CryptGenRandom unter Windows. Die Generierungskosten sind praktisch null.
Das Problem: Zufällige Einfügungen zerstören B-Baum-Indizes
Ein B-Baum-Index hält Daten sortiert. Wenn Sie eine neue Zeile einfügen, findet die Datenbank heraus, wo der neue Schlüssel in die sortierte Reihenfolge passt und platziert ihn dort. Wenn jeder neue Schlüssel zufällig ist, landet er an einer zufälligen Position im Index — was bedeutet, dass jede Einfügung eine andere Seite vom Speicher in den Pufferpool laden muss. Bei geringem Volumen ist das unsichtbar. Bei hohem Volumen (Millionen von Zeilen, hohe INSERT-Rate) entsteht ein Muster namens Index-Fragmentierung: Der Index füllt sich mit halb leeren Seiten, weil jede Einfügung einen anderen Ort trifft, und der Pufferpool dreht sich ständig, weil das heiße Arbeitsset den gesamten Index überspannt anstatt einen vorhersehbaren aktuellen Ausschnitt.
Die Symptome in der Produktion: INSERT-Latenz steigt, Autovacuum-Overhead nimmt zu (PostgreSQL), Checkpoint-Druck wächst, und die Leseleistung für aktuelle Datensätze nimmt ab, weil "aktuell" keiner Lokalität im Index mehr entspricht. Das ist nicht theoretisch — es ist ein gut dokumentierter Schmerzpunkt bei großen PostgreSQL- und MySQL-Tabellen mit UUID v4-Primärschlüsseln.
UUID v7 — Zeitgeordnete Zufälligkeit
UUID v7 wurde explizit entwickelt, um das B-Baum-Fragmentierungsproblem zu lösen. Es kodiert einen 48-Bit-Unix-Millisekunden-Timestamp in den höchstwertigen Bits, gefolgt von Version-Bits, 12 zufälligen Bits, Varianten-Bits und 62 weiteren zufälligen Bits. Gesamte Zufälligkeit: 74 Bits — immer noch weit mehr als zur Kollisionsvermeidung erforderlich.
018f4e6b-a23c-7d45-9abc-0e02b2c3d479
^^^^^^^^^^^^^^
48-bit Unix timestamp (ms precision)
^
version = 7 Da der Timestamp die hohen Bits belegt, sortieren später generierte UUIDs nach früher generierten. Einfügungen werden immer an den rechten Rand des Index angehängt. Die Datenbank muss nur die aktuellste Index-Seite (oder eine kleine Handvoll aktueller Seiten) im Pufferpool halten, nicht den gesamten Index. Bei hohen INSERT-Raten kann das allein die Schreiblatenz um 30–60% reduzieren und I/O um eine Größenordnung bei Tabellen mit Dutzenden Millionen Zeilen senken.
Wie v7 generiert wird
import { v7 as uuidv7 } from 'uuid';
const id = uuidv7();
// => '018f4e6b-a23c-7d45-9abc-0e02b2c3d479'
Dieselbe uuid-Bibliothek hat v7-Unterstützung in Version 10 hinzugefügt. Pythons Standardbibliothek hat es in 3.14 hinzugefügt. PostgreSQL 17 enthält uuidv7() als eingebaute Funktion. Wenn Sie auf einem älteren Stack sind, bieten mehrere kleine Bibliotheken v7-Generierung ohne Abhängigkeiten.
Sub-Millisekunden-Monotonizität
Was passiert, wenn zwei v7-UUIDs innerhalb derselben Millisekunde generiert werden? RFC 9562 erlaubt es Implementierungen, einen monotonen Zähler in den zufälligen Bits zu inkrementieren, um die Reihenfolge innerhalb derselben Millisekunde zu garantieren. Die uuid-Bibliothek macht das standardmäßig. Das Ergebnis: Selbst wenn 10.000 IDs in einer Millisekunde generiert werden, sortieren sie noch korrekt.
Das B-Baum-Index-Fragmentierungsproblem im Detail
Um zu verstehen, warum das wichtig ist, betrachten Sie, was bei 10.000 Einfügungen pro Sekunde mit einem UUID v4-Primärschlüssel auf einer Tabelle mit 100 Millionen vorhandenen Zeilen passiert:
- Jede Einfügung generiert einen zufälligen 128-Bit-Schlüssel, der an einer zufälligen Position unter den 100 Millionen vorhandenen Einträgen landet.
- Die Datenbank muss die spezifische B-Baum-Seite, die diese Position enthält, in den Pufferpool laden.
- Bei 100 Millionen Zeilen und 8-KB-Seiten umspannt der Index ca. 100.000 Seiten. Jede Sekunde werden 10.000 verschiedene Seiten benötigt — weit mehr als ein typischer 8-GB-shared_buffers für diesen Index allein halten kann.
- Jeder Cache-Miss führt zu einem Disk-Read. Bei 10.000 Einfügungen/s kann das Tausende zufälliger Disk-Reads pro Sekunde erzeugen und sogar SSDs bei großen Tabellen saturieren.
Mit UUID v7 landen alle 10.000 Einfügungen pro Sekunde auf der rechtesten Blatt-Seite (oder einer kleinen Handvoll aktueller Seiten). Der Pufferpool muss nur diese wenigen Seiten heiß halten. Die Cache-Hit-Rate für Schreibvorgänge nähert sich 100%. Write Amplification sinkt dramatisch.
Derselbe Vorteil gilt für Bereichsscans: WHERE created_at BETWEEN x AND y auf einer v4-Tabelle erfordert einen vollständigen Index-Scan oder einen separaten Timestamp-Index. Auf einer v7-Tabelle ist der Primärschlüssel selbst der Timestamp-Index — die Abfrage kann direkt zum richtigen Bereich springen.
UUID v7 in PostgreSQL verwenden
-- Works natively with UUID type in PostgreSQL 16+
CREATE TABLE events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- v4 (random)
-- Switch to UUIDv7 via extension or application-side generation
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- With UUIDv7: the timestamp is already embedded, so
-- this separate created_at column is often redundant.
PostgreSQL 17 liefert uuidv7() nativ. Für PostgreSQL 14–16 bietet die Erweiterung pg_uuidv7 dieselbe Funktion. Der UUID-Datentyp speichert v4 und v7 identisch — 16 Bytes, kein Overhead. Der einzige Unterschied ist das Bit-Muster, das die Sortierreihenfolge bestimmt.
Eine nützliche Konsequenz: Da der Timestamp eingebettet ist, brauchen viele Tabellen keine separate created_at-Spalte mehr zum Sortieren oder Anzeigen. Sie können den Timestamp aus einer v7-UUID mit einem einzigen Funktionsaufruf extrahieren. Das reduziert die Schema-Komplexität und eliminiert einen Schreibvorgang pro Einfügung.
UUID v4 vs v7 — Trade-offs
| Eigenschaft | UUID v4 | UUID v7 |
|---|---|---|
| Zufälligkeitsbits | 122 | 74 |
| Sortierreihenfolge | Zufällig | Chronologisch |
| B-Baum INSERT-Leistung | Schlecht (Fragmentierung) | Ausgezeichnet (sequenziell) |
| Timestamp-Offenlegung | Keine | Ja (ms-Präzision) |
| RFC-Standard | RFC 4122 (2005), RFC 9562 (2024) | RFC 9562 (2024) |
| Bibliotheksunterstützung | Universal | Wächst schnell (2024–2026) |
| Eingebettetes created_at | Nein | Ja |
Migrationsanleitung — v4 zu v7
Die Migration einer bestehenden Tabelle von UUID v4 zu v7-Primärschlüsseln ist eine mehrstufige Operation. Die wichtigste Einschränkung: Sie können den Wert eines Primärschlüssels, auf den Fremdschlüssel verweisen, nicht ändern, ohne alle referenzierenden Tabellen ebenfalls zu aktualisieren. Planen Sie ein Wartungsfenster oder verwenden Sie einen Dual-Write-Ansatz.
-- 1. Add new column
ALTER TABLE orders ADD COLUMN id_v7 UUID;
-- 2. Backfill existing rows (preserve original created_at as the
-- timestamp source; use a UUIDv7 library function in your app)
UPDATE orders SET id_v7 = generate_uuidv7(created_at);
-- 3. Verify no NULLs remain
SELECT COUNT(*) FROM orders WHERE id_v7 IS NULL;
-- 4. Swap columns (requires brief maintenance window)
ALTER TABLE orders ALTER COLUMN id_v7 SET NOT NULL;
ALTER TABLE orders ALTER COLUMN id_v7 SET DEFAULT generate_uuidv7(now());
ALTER TABLE orders RENAME COLUMN id TO id_v4_old;
ALTER TABLE orders RENAME COLUMN id_v7 TO id;
ALTER TABLE orders ADD PRIMARY KEY (id); Für Zero-Downtime-Migrationen auf viel genutzten Tabellen lautet der empfohlene Ansatz:
- Neue
id_v7-Spalte mit einem Server-Default hinzufügen, der v7-UUIDs für neue Zeilen generiert. - Alte Zeilen in Batches während Niedriglastzeiten auffüllen, dabei den vorhandenen
created_at-Timestamp als Seed für den v7-Timestamp-Teil verwenden. - Alle Fremdschlüssel-Spalten in referenzierenden Tabellen aktualisieren, um auf
id_v7zu zeigen. - Spalten umbenennen und den alten Primärschlüssel-Constraint während eines kurzen Wartungsfensters löschen.
Der Backfill-Schritt ist der längste. Bei 10.000 Zeilen pro Batch mit 50 ms Pause zwischen den Batches dauert eine 100-Millionen-Zeilen-Tabelle ca. 8 Stunden. Frühzeitig beginnen.
Reale Auswirkungen
Mehrere Engineering-Teams haben Benchmarks veröffentlicht, die UUID v4 und v7 auf Datensätzen in Produktionsgröße vergleichen. Die konsistenten Ergebnisse:
- INSERT-Durchsatz: 2–5x Verbesserung bei Tabellen mit 50M+ Zeilen beim Wechsel von v4 zu v7, wobei der Gewinn mit zunehmender Tabellengröße steigt.
- Schreiblatenz p99: Sinkt von hunderten Millisekunden (v4, unter Last) auf einstellige Millisekunden (v7) auf derselben Hardware.
- Index-Größe: B-Baum-Indizes auf v7-Spalten sind 15–30% kleiner als gleichwertige v4-Indizes auf denselben Daten, weil Fragmentierung weniger halb leere Seiten hinterlässt.
- Pufferpool-Effizienz: Shared-Buffers-Hit-Rate für den Primärschlüssel-Index geht von ~40% (v4, große Tabelle) auf ~99% (v7), weil nur aktuelle Seiten heiß bleiben müssen.
Die Gewinne sind unterhalb ca. 1 Million Zeilen vernachlässigbar. Wenn Ihre Tabelle klein bleibt, bleiben Sie der Einfachheit halber bei v4. Über 10 Millionen Zeilen mit einer nennenswerten INSERT-Rate ist v7 der bessere Standard.
Wann welche Version verwenden
UUID v7 verwenden, wenn:
- Sie ein neues Schema entwerfen und die Tabelle groß werden wird (10M+ Zeilen).
- Die Tabelle eine hohe INSERT-Rate hat — Events, Logs, Bestellungen, Nachrichten, Benachrichtigungen.
- Sie einen Primärschlüssel wollen, der gleichzeitig als Erstellungs-Timestamp dient (eliminiert eine Spalte).
- Sie auf PostgreSQL 17, MySQL 8.0+ oder einem modernen ORM sind, das v7-Generierung unterstützt.
- Das Sortieren nach ID semantisch gleichwertig mit dem Sortieren nach Erstellungszeit ist — was bei den meisten Append-Heavy-Tabellen zutrifft.
UUID v4 verwenden, wenn:
- Der Bezeichner für Nutzer sichtbar ist und die Erstellungszeit nicht preisgeben darf (Einladungscodes, Share-Links, Billing-Handles).
- Die Tabelle klein und stabil ist — kein Leistungsvorteil rechtfertigt die Migrationskosten.
- Sie IDs in einem Kontext generieren, in dem der Erstellungs-Timestamp sensibel ist (private Benutzerdatensätze in einem Produkt, das auf Wachstums-Optik konkurriert).
- Sie etwas als Einmal-Zugangsdaten oder Token benötigen — verwenden Sie einen dedizierten Secret-Generator, keine UUID-Version.
Zusammenfassung
UUID v4 ist zufällig, privat und universal unterstützt. UUID v7 ist zeitgeordnet, datenbankfreundlich und jetzt Standard in den wichtigsten Bibliotheken und Datenbanken. Für neue Schemas mit großen oder schnell wachsenden Tabellen ist v7 der bessere Standard. Für IDs, die Nutzern ausgesetzt sind und bei denen Timestamp-Offenlegung ein Problem ist, bleibt v4 die richtige Wahl.
Generieren Sie beide Versionen sofort mit dem Toova UUID Generator — kein Konto erforderlich. Für kürzere Tokens erzeugt der Random String Generator alphanumerische IDs in beliebiger Länge.