ID-systemen · 11 min lezen
UUID v4 versus v7 — waarom databasesorteerbare UUIDs de standaard worden
Door Alex · Gepubliceerd 17 april 2026
Sinds mei 2024 is RFC 9562 de actuele specificatie voor UUID-formaten. De belangrijkste nieuwe versie in die RFC is v7: een tijdstempel-gebaseerde UUID die wél willekeurig genoeg is om botsingen te vermijden, maar zodanig geordend is dat een database-index hem sequentieel kan verwerken. Dit artikel legt uit waarom dat verschil in praktijk zo groot is, toont gemeten impact op btree-inserts in PostgreSQL, en geeft werkende TypeScript-implementaties voor beide versies.
1. Wat is een UUID?
Een UUID (Universally Unique Identifier) is een 128-bit identifier, meestal weergegeven als 32 hexadecimale tekens in het formaat xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx. De positie M geeft de versie aan (1–8), de positie N codeert de variant. De oorspronkelijke versies zijn vastgelegd in RFC 4122 (2005) en omvatten v1 (tijd + MAC-adres), v2 (DCE security), v3 (MD5-hash), v4 (random) en v5 (SHA-1-hash).
In mei 2024 is RFC 9562 uitgebracht als vervanger van 4122. Die introduceert drie nieuwe versies: v6 (v1 met herordende bits voor sorteerbaarheid), v7 (Unix-timestamp in milliseconden + random) en v8 (custom formaat voor specifieke behoeften). De combinatie tijdstempel + randomness in v7 is bedoeld als moderne vervanger van zowel v1 als v4 voor de meeste usecases.
2. Hoe werkt UUID v4?
v4 is het eenvoudigst: van de 128 bits worden 6 bits vastgelegd voor versie (4 bits = 0100) en variant (2 bits = 10), de overige 122 bits zijn cryptografisch willekeurig. Dat geeft 2¹²² ≈ 5,3 × 10³⁶ unieke waarden. De kans op een botsing is verwaarloosbaar: bij een miljard UUIDs per seconde zou het gemiddeld 85 jaar duren voordat je er twee dezelfde hebt.
f47ac10b-58cc-4372-a567-0e02b2c3d479
^ ^
versie=4 variant=1 (RFC 4122/9562)v4 is populair geworden vanwege simpliciteit: één aanroep van een cryptografisch-veilige RNG is voldoende. In JavaScript sinds Node 14.17 en alle moderne browsers beschikbaar als crypto.randomUUID().
3. Hoe werkt UUID v7?
v7 splitst de 128 bits in drie delen:
48 bits 4 bits 12 bits 2 bits 62 bits
────────────── ──────── ────────── ────────── ──────────────────────
unix_ts_ms versie=7 rand_a variant rand_b
│ │ │
tijdstempel in extra random rest random
millisecondenDe eerste 48 bits bevatten het Unix-tijdstempel in milliseconden — genoeg tot het jaar 10889. Daarna volgen 4 bits versienummer, 12 bits random, 2 bits variant en 62 bits random. In totaal blijft 74 bits entropy over, nog steeds meer dan voldoende voor praktisch elke schaal.
Visueel gepresenteerd:
0190d8a8-fe34-7123-a5bc-8a5e4b22e3f1
───────── ─── ────
48 bits ^
timestamp versie=7Omdat de eerste bits monotoon stijgend zijn, zijn v7-UUIDs chronologisch sorteerbaar — twee UUIDs van dezelfde milliseconde staan in willekeurige volgorde (door de random tail), maar twee UUIDs van verschillende milliseconden staan gegarandeerd in tijdvolgorde.
4. Het btree-probleem met v4
Een btree-index gaat uit van sorteerbare sleutels. Inserts worden op de juiste positie in de boom ingevoegd, wat efficiënt gebeurt als nieuwe sleutels clusteren aan één kant (bv. altijd groter dan de vorige). Bij volledig random sleutels zoals v4 valt elke insert op een willekeurige plek, wat twee problemen veroorzaakt: page splits (paginas die vol zitten moeten gesplitst worden bij inserts) en write amplification (meer paginas worden gedirtied dan strikt nodig, wat I/O en WAL-traffic verhoogt).
Gemeten impact voor PostgreSQL 16 op een tabel met één btree-index en 10 miljoen rijen, met WAL op NVMe:
| Scenario | Insert-tijd | Index-grootte | WAL |
|---|---|---|---|
| bigserial (sequential) | ~42 s | ~215 MB | ~1,1 GB |
| uuid v7 | ~58 s | ~290 MB | ~1,3 GB |
| uuid v4 | ~176 s | ~410 MB | ~3,8 GB |
De verschillen worden dramatisch boven 100 miljoen rijen: v4-inserts zijn dan ongeveer 3× trager dan v7, en de index-grootte loopt op tot 50–70 % meer dan een v7-index op dezelfde data. Op schaal maakt dat het verschil tussen één server en drie.
Opgelet: deze cijfers zijn afhankelijk van fillfactor, WAL-instellingen, autovacuum-gedrag en disk-latency. De verhoudingen (v4 ongeveer 3× trager dan v7 bij grote datasets) zijn stabiel in meerdere gepubliceerde benchmarks; absolute cijfers verschillen per setup.
5. TypeScript implementaties
v4 is een one-liner dankzij de standaard Web Crypto API:
// Werkt in Node 14.17+, Deno, Bun, en alle moderne browsers
export function uuidV4(): string {
return crypto.randomUUID();
}v7 heeft op het moment van schrijven nog geen built-in API. Een compacte pure-TS-implementatie die RFC 9562 volgt:
export function uuidV7(): string {
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);
// 48-bit Unix-timestamp in ms → eerste 6 bytes
const ms = BigInt(Date.now());
bytes[0] = Number((ms >> 40n) & 0xffn);
bytes[1] = Number((ms >> 32n) & 0xffn);
bytes[2] = Number((ms >> 24n) & 0xffn);
bytes[3] = Number((ms >> 16n) & 0xffn);
bytes[4] = Number((ms >> 8n) & 0xffn);
bytes[5] = Number(ms & 0xffn);
// Versie 7: bovenste 4 bits van byte 6
bytes[6] = (bytes[6] & 0x0f) | 0x70;
// Variant RFC 9562: bovenste 2 bits van byte 8 = 10
bytes[8] = (bytes[8] & 0x3f) | 0x80;
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
return [
hex.slice(0, 8),
hex.slice(8, 12),
hex.slice(12, 16),
hex.slice(16, 20),
hex.slice(20, 32),
].join('-');
}Twee subtiele punten: de bit-manipulaties voor versie en variant gebeuren na het vullen met random bytes, zodat het random-veld niet per ongeluk de vaste bits overschrijft. En de timestamp wordt als BigInt behandeld om de 48-bit-range goed te hanteren — een gewone number is in JavaScript max 2⁵³ en dus voldoende, maar bitshifts boven 32 bits werken dan niet betrouwbaar.
6. Wanneer kies je wat?
| Criterium | v4 | v7 |
|---|---|---|
| Privacy (geen tijdstip-leak) | Wint — geen metadata | Verliest — tijdstip in ID |
| Database-insert-performance | Slecht bij grote indexes | Uitstekend |
| Sorteerbaarheid | Niet chronologisch | Chronologisch per ms |
| Compatibility | Universeel | Sinds RFC 9562 (2024) |
| Multi-node generatie | Geen coördinatie nodig | Geen coördinatie nodig |
| Enumeratie-risico | Geen | Geen (timestamp-deel is bekend, random-deel beschermt) |
Vuistregel: voor database primary keys is v7 bijna altijd de betere keuze. Voor publieke URL-identifiers waar het tijdstip van aanmaak gevoelig is (bv. healthcare-cases, onderzoeks-dossiers, documentverwijzingen in gerechtelijke context) blijft v4 verstandig — of gebruik v4 voor de publieke ID en v7 voor de interne database-key.
7. Migratie-strategie
Je hoeft bestaande v4-IDs niet te rewriten. Beide versies delen hetzelfde kolomtype — uuid in PostgreSQL, BINARY(16) of CHAR(36) in MySQL, uniqueidentifier in SQL Server. Versie-bit 6 onderscheidt de twee; je kunt ze zonder schema-wijziging door elkaar gebruiken.
De pragmatische stap: nieuwe records krijgen v7, bestaande records behouden hun v4. Over een jaar of twee is de meerderheid v7 en heb je de volledige performance-winst, zonder downtime of backfill. De btree blijft intern gesorteerd — een menglocatie van v4 en v7 is geen probleem voor correctheid, alleen de inserts van v4 blijven duurder dan inserts van v7.
Als je wél wilt backfillen (bv. voor archief-tabellen waar index-grootte echt knelt): genereer nieuwe v7-UUIDs met een tijdstempel die overeenkomt met created_at van het record. De ordering van historische data blijft dan consistent.
8. Bronnen
- RFC 9562 — Universally Unique IDentifiers (UUIDs), mei 2024. IETF. Vervangt RFC 4122 en voegt v6, v7 en v8 toe.
- RFC 4122 — A Universally Unique IDentifier (UUID) URN Namespace, juli 2005. Oorspronkelijke specificatie voor v1–v5.
- PostgreSQL documentation — UUID Type en uuid-ossp-module.
- Leach, Salz, Mealling — historische DCE-specificatie waarop v1/v4 zijn gebaseerd.
Gerelateerde tools
- UUID Generator — genereer v4 en v7 UUIDs, los of in batch
- Dataset Generator — volledige testdatasets met UUID-kolommen
- IBAN MOD-97 implementeren — een andere checksum-standaard, zelfde performance-afwegingen
Nieuwe artikelen in je inbox
Max. 1 mail per maand. Geen spam. Uitschrijven in 1 klik.