UUID vs. ULID vs. Nano ID vs. Auto-Increment-IDs
Fünf verbreitete ID-Verfahren im Vergleich, und zwar bei dem, was in der Praxis zählt: Größe, Sortierung, Kollisionsrisiko, Verhalten in der Datenbank und was eine ID preisgibt.
Die Wahl eines ID-Formats läuft auf drei Fragen hinaus. Müssen sich IDs nach dem Erstellungszeitpunkt sortieren lassen? Tauchen sie in URLs oder öffentlichen APIs auf? Und speichert die Datenbank sie in einer nativen 128-Bit-Spalte oder als Text? UUID v7, im Mai 2024 in RFC 9562 standardisiert, hat die Antwort für viele Projekte verändert: Sie bietet die zeitliche Sortierung, die ULID populär gemacht hat, in einem Format, das jede UUID-Spalte und jede Bibliothek bereits akzeptiert.
Auf einen Blick
| Auto-Increment | UUID v4 | UUID v7 | ULID | Nano ID | |
|---|---|---|---|---|---|
| Beispiel | 1042 | f47ac10b-58cc-4372-a567-0e02b2c3d479 | 01926f3a-7c2b-7d4e-9a1f-3b5c8d2e6f10 | 01J9QKMZ1BFN79M7SVBJ6JWVRG | V1StGXR8_Z5jdHi6B-myT |
| Binäre Größe | 4 oder 8 Bytes | 16 Bytes | 16 Bytes | 16 Bytes | Als Text gespeichert |
| Textlänge | Bis zu 10 oder 19 Ziffern | 36 | 36 | 26 | Standardmäßig 21 |
| Zufallsbits | Keine | 122 | Bis zu 74 | 80 | Standardmäßig 126 |
| Nach Erstellungszeit sortierbar | Ja | Nein | Ja, auf die Millisekunde | Ja, auf die Millisekunde | Nein |
| Ohne Datenbank erzeugbar | Nein | Ja | Ja | Ja | Ja |
| Spezifikation | Datenbankfunktion | RFC 9562 | RFC 9562 | Community-Spezifikation | Bibliothek |
| Verrät | Anzahl und Reihenfolge | Nichts | Erstellungszeit | Erstellungszeit | Nichts |
Wie die einzelnen IDs aufgebaut sind
- Auto-Increment ist ein Zähler, den die Datenbank führt. Ein vorzeichenbehaftetes 32-Bit-
INTendet bei 2.147.483.647, ein vorzeichenbehaftetesBIGINTbei 9.223.372.036.854.775.807. Die Anwendung erfährt die ID erst nach dem Insert, es sei denn, sie reserviert vorher Werte aus einer Sequenz. - UUID v4 besteht aus 122 Zufallsbits und 6 festen Bits, die Version und Variante kennzeichnen.
- UUID v7 beginnt mit einem 48-Bit-Unix-Zeitstempel in Millisekunden, gefolgt von der Version, 12 Bits namens
rand_a, der Variante und 62 Bits namensrand_b. RFC 9562 erlaubt einem Generator, einen Teil dieser Bits als Zähler zu nutzen, damit IDs aus derselben Millisekunde trotzdem in der richtigen Reihenfolge entstehen. Der Generator auf dieser Website macht das mit den 12 Bits vonrand_a. - ULID besteht aus einem 48-Bit-Zeitstempel in Millisekunden und 80 Zufallsbits, geschrieben als 26 Zeichen in Crockfords Base32: 10 für die Zeit und 16 für den Zufall. Das Alphabet lässt I, L, O und U weg und unterscheidet nicht zwischen Groß- und Kleinschreibung. Im monotonen Modus übernimmt eine ULID, die in derselben Millisekunde wie die vorige entsteht, deren Zufallsteil und erhöht ihn um eins.
- Nano ID ist eine Folge zufälliger Zeichen, standardmäßig 21 Stück aus dem 64 Zeichen umfassenden Alphabet
A-Z a-z 0-9 _ -. Jedes Zeichen trägt 6 Bits, das ergibt 126 Bits. Länge und Alphabet lassen sich ändern, und die Zahl der Zufallsbits ändert sich entsprechend mit.
Eine ULID und eine UUID sind beide 128-Bit-Zahlen, nur unterschiedlich geschrieben. 01J9QKMZ1BFN79M7SVBJ6JWVRG und 01926f3a-7c2b-7d4e-9a1f-3b5c8d2e6f10 sind derselbe Wert, einmal in Base32 und einmal in Hex. Deshalb lässt sich eine ULID in einer nativen UUID-Spalte speichern, etwa im Typ uuid von PostgreSQL, und behält dort ihre Sortierung. Eine ULID, die nicht als v7-UUID entstanden ist, hat an den Stellen für Version und Variante allerdings beliebige Bits, sodass ein strenger UUID-Validator sie womöglich ablehnt.
Kollisionsrisiko, nachgerechnet
Für rein zufällige IDs gilt die Geburtstagsnäherung: Bei n IDs aus N = 2 hoch Anzahl der Zufallsbits möglichen Werten liegt die Wahrscheinlichkeit für mindestens ein Duplikat bei etwa 1 − e^(−n²/2N) und erreicht 50 Prozent bei ungefähr n = 1,18 × √N.
| Verfahren | Zufallsbits | IDs für 50% Duplikat-Wahrscheinlichkeit | Duplikat-Wahrscheinlichkeit bei 1 Milliarde IDs |
|---|---|---|---|
| UUID v4 | 122 | 2,7 × 10^18 | 9,4 × 10^-20 |
| Nano ID, 21 Zeichen | 126 | 1,1 × 10^19 | 5,9 × 10^-21 |
| Nano ID, 10 Zeichen | 60 | 1,3 × 10^9 | Etwa 35% |
Die letzte Zeile sollten Sie sich merken: Wer eine Nano ID für hübschere URLs kürzt, verliert sehr schnell an Kollisionssicherheit. Rechnen Sie also nach, bevor Sie sich für eine Länge entscheiden.
Bei UUID v7 und ULID können zwei IDs nur kollidieren, wenn sie dieselbe Millisekunde tragen. n ist hier also die Zahl der IDs, die in einer Millisekunde von Generatoren ohne gemeinsamen Zähler erzeugt werden. Bei 1.000 solcher IDs in derselben Millisekunde liegt die Wahrscheinlichkeit für ein Duplikat bei etwa 2,6 × 10^-17 mit 74 Zufallsbits und bei 4,1 × 10^-19 mit den 80 Bits von ULID. In der Praxis ist das Risiko bei allen Verfahren hier eine schwache Zufallsquelle wie Math.random, nicht die Mathematik.
Verhalten im Datenbankindex
Primärschlüssel liegen meist in einem B-Baum-Index. Ist jeder neue Schlüssel größer als der vorige, wie bei Auto-Increment, UUID v7 und ULID, landen Inserts am rechten Rand des Index, Seiten werden vollständig gefüllt, und kürzlich geschriebene Zeilen bleiben nah beieinander. Zufällige Schlüssel, also UUID v4 und Nano ID, landen irgendwo: Seiten werden geteilt, bleiben teilweise leer, und sobald der Index nicht mehr in den Arbeitsspeicher passt, kann jeder Insert einen zufälligen Lesezugriff bedeuten.
Stärker ist der Effekt in MySQL und MariaDB mit InnoDB, die die Zeilen selbst in Primärschlüssel-Reihenfolge speichern. Zufällige Schlüssel verstreuen dort also die ganze Tabelle und nicht nur den Index. PostgreSQL hält die Zeilen in einem separaten Heap, der Unterschied beschränkt sich dort auf den Index.
Das Speicherformat ist genauso wichtig wie das Verfahren:
- Speichern Sie UUIDs und ULIDs in einem nativen 16-Byte-Typ, wo es einen gibt:
uuidin PostgreSQL,UUIDin MariaDB ab 10.7,BINARY(16)in MySQL.CHAR(36)braucht mehr als doppelt so viel Platz und vergrößert jeden Index. - Übergeben Sie in MySQL bei v7-Werten nicht das Swap-Flag an
UUID_TO_BIN(uuid, 1). Es verschiebt die Zeitfelder zugunsten von v1-UUIDs und würde die Sortierung von v7 zerstören. uniqueidentifierin SQL Server vergleicht die letzten sechs Bytes zuerst, deshalb sortieren sich dort gespeicherte v7-Werte nicht nach der Zeit. Die Details stehen in UUID vs. GUID.- Nano IDs sind Strings und werden als
CHARoderVARCHARgespeichert, mit binärer Kollation, falls Groß- und Kleinschreibung eine Rolle spielen muss.
URLs und was eine ID preisgibt
Alle fünf Formate lassen sich ohne Prozentkodierung in einer URL verwenden: Hex-Ziffern, Bindestriche, Crockford-Base32 und das Nano-ID-Alphabet bestehen ausschließlich aus nicht reservierten Zeichen nach RFC 3986. Der Unterschied liegt in der Länge (36, 26 oder 21 Zeichen) und in der Groß- und Kleinschreibung. UUIDs und ULIDs unterscheiden sie nicht und überstehen daher Systeme, die die Schreibung ändern; Nano IDs dagegen nicht.
Was eine ID verrät, gibt oft den Ausschlag:
- Auto-Increment verrät Mengen. Hat Ihre Rechnung heute die Nummer 1042 und eine Woche später 1342, hat das Unternehmen in dieser Woche rund 300 Rechnungen ausgestellt. Fortlaufende IDs machen es außerdem trivial, benachbarte Datensätze auszuprobieren, wodurch jede fehlende Zugriffsprüfung zum Datenleck wird.
- UUID v7 und ULID verraten, wann der Datensatz erstellt wurde, und zwar auf die Millisekunde genau. Bei Bestellungen ist das selten ein Problem, bei Benutzerkonten oder medizinischen Daten kann es eines sein. Fügen Sie eine v7-UUID in den Prüfbereich des UUID-v7-Generators ein, und er zeigt den Zeitstempel an.
- UUID v4 und Nano ID verraten nichts darüber, wann oder wo sie erzeugt wurden.
Keine dieser IDs ist ein Geheimnis. Eine nicht erratbare ID erschwert das Ausprobieren, ersetzt aber nicht die Prüfung, ob der Nutzer den Datensatz sehen darf.
Welche ID Sie wählen sollten
- Interne Tabellen in einer einzigen Datenbank, die Nutzer nie zu sehen bekommen: Auto-Increment
BIGINT. Das ist der kleinste und einfachste Schlüssel. - Primärschlüssel, die von der Anwendung oder über mehrere Dienste hinweg erzeugt werden, sofern eine native UUID-Spalte verfügbar ist: UUID v7.
- Öffentliche Kennungen, bei denen die Erstellungszeit privat bleiben muss, Korrelations-IDs und Dateinamen: UUID v4, aus dem UUID-v4-Generator oder von
crypto.randomUUID(). - Kurze IDs in URLs, bei denen die Reihenfolge keine Rolle spielt: Nano ID in Standardlänge oder nahe daran.
- ULID, wenn Ihr Stack sie bereits verwendet oder Sie die kürzere Textform ohne Unterscheidung der Groß- und Kleinschreibung möchten. Für neue Projekte deckt UUID v7 dasselbe in einem Standardformat ab.
Eine verbreitete Kombination vereint das Beste aus beiden Welten: ein Primärschlüssel als BIGINT oder UUID v7 für Joins und Indizes, dazu eine separate zufällige öffentliche ID mit eindeutigem Index für URLs und APIs.