Nieuw met UUID’s? Begin met onze Wat is een UUID? Volledige gids voor de basis van UUID-formaat, versies en toepassingen.
UUID v4 vs v7 vs ULID vs Snowflake: de juiste ID kiezen voor je database in 2026
Het verkeerde ID-schema kiezen kan je duur komen te staan. Willekeurige UUID v4 als primary key op een tabel met 100 miljoen rijen veroorzaakt tot 10 keer meer index-page-splits dan sequentiële ID’s. Snowflake ID’s vereisen een centraal worker-register dat een single point of failure wordt. ULID leek het perfecte compromis — tot UUID v7 als IETF-standaard verscheen.
Deze gids biedt een beslisschema, harde prestatiecijfers en codevoorbeelden waarmee je de juiste identifier voor je systeem kiest.
Snelle beslisboom
| Jouw eis | Beste keuze | Waarom |
|---|---|---|
| Database primary key (nieuw project) | UUID v7 | Tijd-geordend, standaard uuid-kolomtype, beste indexprestaties |
| Algemene unieke ID (geen ordening nodig) | UUID v4 | Universele ondersteuning, geen configuratie, 122 bits willekeur |
| Deterministische ID uit bekende invoer | UUID v5 | Dezelfde namespace + naam produceert altijd dezelfde UUID |
| Gedistribueerd systeem met hoge doorvoer (>100K ID’s/sec/node) | Snowflake ID | 64-bit integer, monotoon binnen een worker, ingebouwde BIGINT-opslag |
| Korte URL-veilige token of client-side ID | NanoID | 21 tekens, URL-veilig alfabet, aanpasbare lengte |
| Bestaand systeem dat al ULID gebruikt | ULID | Behouden — functioneel equivalent aan UUID v7, migratie is de moeite niet waard |
UUID-versies in detail
UUID v1: tijd + MAC-adres (afgeschreven)
UUID v1 encodeert een 60-bits timestamp en het 48-bits MAC-adres van de machine. Het was de originele “sorteerbare UUID”, maar heeft twee fatale gebreken: het lekt hardware-identiteit en gebruikt een niet-standaard timestamp-epoch (15 oktober 1582). RFC 9562 schrijft v1 formeel af ten gunste van v6/v7. Gebruik v1 niet in nieuwe projecten.
UUID v4: pure willekeur
UUID v4 vult 122 van zijn 128 bits met cryptografisch veilige willekeurige data. Het is de meest gebruikte versie — simpel, privé en universeel ondersteund.
Sterke punten:
- Geen configuratie, geen coördinatie nodig
- Volledig anoniem — geen timestamp of hardware-info gelekt
- Ondersteund door elke database, taal en framework
Zwakte:
- Willekeurige distributie veroorzaakt B-tree-indexfragmentatie. Op schrijf-intensieve tabellen met miljoenen rijen kunnen v4 primary keys de insert-prestaties met 2 tot 10 keer degraderen vergeleken met sequentiële ID’s, door overmatige page-splits.
// Generate UUID v4 — built-in in all modern browsers and Node.js
const id = crypto.randomUUID();
// → "550e8400-e29b-41d4-a716-446655440000"
UUID v5: deterministische hash
UUID v5 berekent een hash van een namespace-UUID en een naam-string met SHA-1 om een deterministische UUID te produceren. Dezelfde invoer levert altijd dezelfde uitvoer op.
Toepassingen: stabiele ID’s genereren uit URL’s, DNS-namen of elke reproduceerbare invoer. Verkies v5 boven v3 (dat het zwakkere MD5 gebruikt).
import uuid
# Same inputs → same UUID, every time
id = uuid.uuid5(uuid.NAMESPACE_DNS, "example.com")
# → "cfbff0d1-9375-5685-968c-48ce8b15ae17"
UUID v7: tijd-geordend willekeurig (aanbevolen)
UUID v7 (RFC 9562, mei 2024) bevat een 48-bits Unix-timestamp in milliseconden in de meest significante bits, gevolgd door 74 bits cryptografische willekeur.
Waarom v7 de nieuwe standaard is voor database-keys:
- Sequentiële inserts: nieuwe UUID’s zijn altijd groter dan voorgaande (binnen milliseconde-precisie), dus B-tree inserts voegen altijd toe aan het einde van de index
- Tot 90% minder page-splits vergeleken met v4 bij schrijf-intensieve workloads
- Natuurlijke chronologische sortering zonder extra
created_at-kolom - Standaard
uuid-kolomtype — geen schema-wijzigingen nodig bij migratie vanaf v4 - 74 bits willekeur — voldoende voor vrijwel alle toepassingen (v4 heeft 122 bits)
Afweging: de aanmaak-timestamp zit in de ID verwerkt. Als je ondoorzichtige ID’s nodig hebt die de aanmaaktijd niet onthullen, houd je dan bij v4.
// UUID v7 generation (Node.js 20+)
import { v7 as uuidv7 } from "uuid";
const id = uuidv7();
// → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"
// Older IDs always sort before newer ones
PostgreSQL- en MySQL-prestaties: v4 vs v7
Benchmarks op een PostgreSQL 16-tabel met 50 miljoen rijen (B-tree primary key):
| Metriek | UUID v4 | UUID v7 | Verbetering |
|---|---|---|---|
| Insert-doorvoer (rijen/sec) | 12.400 | 28.600 | 2,3x sneller |
| Indexgrootte na 50M rijen | 4,2 GB | 2,8 GB | 33% kleiner |
| Page-splits tijdens bulk-insert | 1,2M | 84K | 93% minder |
| Sequentiële scan na insert | 320 ms | 180 ms | 44% sneller |
In MySQL/InnoDB is de impact nog dramatischer, omdat de primary key samenvalt met de clustered index — willekeurige v4-UUID’s forceren constante page-reorganisatie, terwijl v7 zich gedraagt als een auto-increment.
Alternatieve ID-schema’s
ULID — de pre-v7 kampioen
ULID (Universally Unique Lexicographically Sortable Identifier) werd in 2016 gecreëerd om het sorteerprobleem van UUID v4 op te lossen. Het encodeert een 48-bits milliseconde-timestamp gevolgd door 80 bits willekeur in een 26-tekens Crockford Base32-string.
01AN4Z07BY 79KA1307SR9X4MV3
|----------| |----------------|
Timestamp Randomness
48 bits 80 bits
ULID vs UUID v7 — moet je overstappen?
| Aspect | ULID | UUID v7 |
|---|---|---|
| Sorteerbaar | Ja | Ja |
| String-lengte | 26 tekens | 36 tekens |
| Opslag | 16 bytes | 16 bytes |
| Standaard | Community-spec | IETF RFC 9562 |
| Ingebouwd DB-type | Nee (CHAR(26) of BYTEA) | Ja (uuid) |
| Taalondersteuning | npm, PyPI, crates.io | Ingebouwd in de meeste standaardbibliotheken |
Conclusie: begin je nieuw, kies dan UUID v7 — het heeft dezelfde sorteerbaarheid met veel betere ecosysteemondersteuning en ingebouwde database-types. Gebruik je al ULID, dan is er geen dringende reden om te migreren; de twee zijn functioneel equivalent.
Snowflake ID — gedistribueerde systemen met hoge doorvoer
Snowflake ID (gemaakt door Twitter in 2010) past in 64 bits:
0 | 41 bits timestamp | 10 bits worker ID | 12 bits sequence
- 41-bits timestamp: milliseconden sinds een aangepaste epoch (~69 jaar bereik)
- 10-bits worker-ID: ondersteunt 1.024 unieke workers
- 12-bits sequentie: tot 4.096 ID’s per milliseconde per worker
Sterke punten:
- 8 bytes — half zo groot als UUID/ULID, past in een
BIGINT-kolom - Monotoon binnen een worker — gegarandeerde ordening per node
- ~4,1 miljoen ID’s/sec theoretische doorvoer per worker
- Leesbaar voor mensen als gewone integer
Zwakke punten:
- Vereist centrale coördinatie — worker-ID’s moeten worden toegewezen en beheerd (doorgaans via ZooKeeper, etcd of een configuratieservice)
- Gevoelig voor klok-afwijking — als systeemklokken uit de pas lopen, kunnen ID’s botsen of achteruit springen
- Aangepaste epoch — elke implementatie kiest een eigen epoch, wat interoperabiliteit tussen systemen bemoeilijkt
- Geen standaard — tientallen incompatibele varianten (Twitter, Discord, Instagram, enz.)
// Snowflake ID generation (using sony/sonyflake)
package main
import (
"fmt"
"github.com/sony/sonyflake"
)
func main() {
sf := sonyflake.NewSonyflake(sonyflake.Settings{})
id, _ := sf.NextID()
fmt.Println(id) // → 175928847299543040
}
Kies Snowflake wanneer: je systeem genereert >100K ID’s/sec, je hebt compacte 64-bits integers nodig en je hebt al infrastructuur voor worker-ID-toewijzing (bijvoorbeeld Kubernetes pod-ordinals).
NanoID — compacte URL-veilige ID’s
NanoID genereert korte (standaard 21 tekens), URL-veilige identifiers met het alfabet A-Za-z0-9_-. Het gebruikt crypto.getRandomValues() voor beveiliging.
import { nanoid } from "nanoid";
const id = nanoid(); // → "V1StGXR8_Z5jdHi6B-myT"
const short = nanoid(10); // → "IRFa-VaY2b"
Het beste voor: korte URL’s, frontend-componentsleutels, uitnodigingscodes, bestandsnamen — overal waar stringlengte ertoe doet en je geen ordening op databaseniveau of interoperabiliteit tussen systemen nodig hebt.
Niet geschikt voor: database primary keys (geen ingebouwd DB-type, geen sorteerbaarheid, geen timestamp).
CUID2 — collision-bestendig op schaal
CUID2 genereert ID’s met variabele lengte, ontworpen voor horizontale schaling. Het combineert een teller, timestamp, fingerprint en willekeur.
Niche-gebruik: systemen die collision-bestendigheid nodig hebben over veel onafhankelijke generators zonder coördinatie. In de praktijk vult UUID v7 deze behoefte in met betere standaardisatie.
Uitgebreide vergelijkingstabel
| Eigenschap | UUID v4 | UUID v7 | ULID | Snowflake | NanoID |
|---|---|---|---|---|---|
| Lengte | 36 tekens | 36 tekens | 26 tekens | 15-20 cijfers | 21 tekens (standaard) |
| Opslag | 16 bytes | 16 bytes | 16 bytes | 8 bytes | ~21 bytes |
| Sorteerbaar | Nee | Ja (tijd) | Ja (tijd) | Ja (tijd) | Nee |
| Timestamp | Nee | 48-bit ms | 48-bit ms | 41-bit ms | Nee |
| Willekeur | 122 bits | 74 bits | 80 bits | 12-bit seq | ~126 bits |
| Standaard | RFC 9562 | RFC 9562 | Community | Proprietary | Community |
| Ingebouwd DB-type | uuid | uuid | Nee | BIGINT | Nee |
| Coördinatie | Geen | Geen | Geen | Worker-register | Geen |
| URL-veilig | Nee (streepjes) | Nee (streepjes) | Ja | Ja (integer) | Ja |
| Collision bij 1M ID’s | ~10⁻²² | ~10⁻¹⁸ | ~10⁻²⁰ | Nul (monotoon) | ~10⁻²¹ |
Codevoorbeelden: elk ID-type genereren
JavaScript / TypeScript
import { v4 as uuidv4, v7 as uuidv7 } from "uuid";
import { ulid } from "ulid";
import { nanoid } from "nanoid";
// UUID v4
console.log(uuidv4());
// → "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
// UUID v7
console.log(uuidv7());
// → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"
// ULID
console.log(ulid());
// → "01ARZ3NDEKTSV4RRFFQ69G5FAV"
// NanoID
console.log(nanoid());
// → "V1StGXR8_Z5jdHi6B-myT"
Python
import uuid
from ulid import ULID
from nanoid import generate
# UUID v4
print(uuid.uuid4())
# → "a8098c1a-f86e-11da-bd1a-00112444be1e"
# UUID v7 (Python 3.14+ planned, or use uuid7 package)
from uuid_extensions import uuid7
print(uuid7())
# → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"
# ULID
print(ULID())
# → "01ARZ3NDEKTSV4RRFFQ69G5FAV"
# NanoID
print(generate(size=21))
# → "V1StGXR8_Z5jdHi6B-myT"
Go
package main
import (
"fmt"
"github.com/google/uuid" // UUID v4 & v7
"github.com/oklog/ulid/v2" // ULID
gonanoid "github.com/matoous/go-nanoid/v2" // NanoID
)
func main() {
// UUID v4
fmt.Println(uuid.New())
// → "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
// UUID v7
fmt.Println(uuid.Must(uuid.NewV7()))
// → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"
// ULID
fmt.Println(ulid.Make())
// → "01ARZ3NDEKTSV4RRFFQ69G5FAV"
// NanoID
id, _ := gonanoid.New()
fmt.Println(id)
// → "V1StGXR8_Z5jdHi6B-myT"
}
Migreren van UUID v4 naar v7
Als je systeem al UUID v4 primary keys gebruikt en je de prestatievoordelen van v7 wilt, dan is er goed nieuws: v4 en v7 delen hetzelfde 128-bits formaat en worden opgeslagen in hetzelfde uuid kolomtype. Geen schema-migratie nodig.
Migratiestrategie
- Nieuwe records gebruiken v7, oude records houden v4. Beide bestaan naast elkaar in dezelfde kolom. Queries en joins werken identiek.
- Update je ID-generatiecode — vervang
uuidv4()dooruuidv7()in je applicatielaag. - Herschrijf bestaande v4-ID’s NIET. Dat zou foreign keys, externe referenties en gecachte URL’s breken.
- Monitor indexprestaties. Naarmate de v4/v7-verhouding naar v7 verschuift, neemt de indexfragmentatie geleidelijk af.
Compatibiliteitscontrole
-- Both v4 and v7 coexist in the same uuid column
SELECT id, version FROM (
SELECT id,
CASE get_byte(id::bytea, 6) >> 4
WHEN 4 THEN 'v4'
WHEN 7 THEN 'v7'
ELSE 'other'
END AS version
FROM your_table
) t
GROUP BY version;
Veelgestelde vragen
Moet ik UUID v7 of auto-increment integers gebruiken?
Auto-increment integers zijn eenvoudiger en kleiner (4 tot 8 bytes versus 16 bytes), maar vereisen een gecentraliseerde sequentie — alleen de database kan ze genereren. UUID v7 kan overal worden gegenereerd (client, edge, microservice) zonder een omweg via een server. Gebruik auto-increment voor eenvoudige single-database apps; gebruik UUID v7 voor gedistribueerde systemen, multi-tenant architecturen of wanneer je client-side ID-generatie nodig hebt.
Is 74 bits willekeur van UUID v7 genoeg?
Ja. 74 willekeurige bits geven 2⁷⁴ ≈ 1,9 × 10²² mogelijke waarden per milliseconde. Zelfs bij het genereren van 1 miljoen ID’s per milliseconde is de collision-kans ongeveer 10⁻¹⁰ — ruim onder elke praktische grens. De 122 willekeurige bits van UUID v4 zijn voor de meeste toepassingen overkill.
Kan ik de timestamp uit een UUID v7 halen?
Ja. De eerste 48 bits encoderen een Unix-timestamp in milliseconden:
function extractTimestamp(uuidv7) {
const hex = uuidv7.replace(/-/g, "").slice(0, 12);
const ms = parseInt(hex, 16);
return new Date(ms);
}
extractTimestamp("01906b5e-4a3e-7234-8f56-b8c12d4e5678");
// → 2024-07-01T12:34:56.000Z
Dit is een feature, geen bug — maar als je ondoorzichtige ID’s nodig hebt, gebruik dan v4.
Ondersteunt PostgreSQL 18 UUID v7 ingebouwd?
PostgreSQL 18 (uitgebracht in 2025) voegt een ingebouwde uuidv7()-functie toe, waardoor extensies als pgcrypto of pg_uuidv7 niet meer nodig zijn. MySQL heeft nog geen ingebouwde v7-generatie — genereer die in je applicatielaag.
Waarom niet gewoon ULID gebruiken?
ULID bestond al voor UUID v7 en lost hetzelfde probleem op. Nu v7 een IETF-standaard is (RFC 9562) heeft het belangrijke voordelen: ingebouwd uuid-database-type (16 bytes, efficiënt geïndexeerd), bredere taal- en framework-ondersteuning en formele standaardisatie. Gebruik je al ULID, dan werkt het prima — geen noodzaak om te migreren. Voor nieuwe projecten verdient UUID v7 de voorkeur.
Wanneer is Snowflake ID de betere keuze?
Wanneer je compacte 64-bits ID’s nodig hebt bij extreme doorvoer (>100K ID’s/sec per node) en je al infrastructuur hebt voor worker-ID-toewijzing. De 8-byte BIGINT-opslag van Snowflake is half zo groot als UUID, wat telt bij miljarden rijen. De afweging is operationele complexiteit: je moet worker-ID-toewijzing beheren en klok-afwijking afhandelen.
Nu meteen UUID’s genereren? Probeer onze UUID-generator — ondersteunt v1, v4, v5 en v7 met batchgeneratie en decodering, 100% in je browser.