Comparison
UUID v4 vs v7: scegli v7 per le chiavi del database
v4 per token opachi. v7 per tutto il resto, specialmente le chiavi del database.
By Buğra SözeriPublished
TL;DR.UUID v4 è 122 bit di puro random e non ordinabile, il che distrugge le prestazioni dell’indice B-tree quando usato come chiave del database. UUID v7 (RFC 9562, 2024) antepone un timestamp Unix a 48 bit in millisecondi così gli ID si ordinano per data di creazione. Usa v7 per le chiavi del database e gli ID degli eventi; usa v4 per i token di sessione opachi.
Gli UUID sono identificatori a 128 bit progettati per essere globalmente unici tra sistemi senza coordinamento. La specifica originale (RFC 4122, 2005) definiva cinque versioni; v4 (puro random) era l’unica che la maggior parte degli sviluppatori usava. RFC 9562 (2024) ha aggiunto v7, e v7 dovrebbe essere il nuovo default per quasi ogni caso d’uso in cui v4 era scelto in precedenza.
Le differenze principali
| Proprietà | v4 | v7 |
|---|---|---|
| Specificato | RFC 4122 (2005) | RFC 9562 (2024) |
| Bit casuali | 122 | 74 |
| Timestamp incorporato | No | Sì (48 bit ms dall’epoca) |
| Ordinabile per data di creazione | No (puramente casuale) | Sì (lessicografico = cronologico) |
| Probabilità di collisione | ~1 su 10³⁶ per qualsiasi coppia | Effettivamente zero (timestamp + random combinati) |
| Compatibilità indice database | Scarsa (inserimento casuale agita i B-tree) | Eccellente (sequenziale, append-friendly) |
| Supporto browser | crypto.randomUUID() dal 2021 | Generazione manuale (ancora nessuna API nativa) |
Perché v4 danneggia i database
Le chiavi primarie del database vivono tipicamente in un indice B-tree. I B-tree funzionano meglio quando gli inserimenti arrivano in ordine ordinato: le nuove chiavi si aggiungono alla foglia più a destra, nessun ribilanciamento necessario, la pagina dell’indice rimane calda in cache.
Le chiavi casuali (v4) distruggono tutto questo. Ogni inserimento atterra in una pagina casuale, potenzialmente causando una divisione di pagina, certamente causando cache miss, e producendo spazio libero sparso nell’indice che non viene mai compattato. Per i carichi di lavoro ad alta scrittura questo si manifesta come:
- I/O 10-100× più alto delle chiavi sequenziali allo stesso ritmo di scrittura
- File di indice gonfiati (spesso 2-3× più grandi di quanto sarebbero con chiavi ordinate)
- Rallentamenti delle query man mano che la percentuale di cache hit degrada
I benchmark Postgres-vs-MySQL che confrontano UUID v4 con le chiavi primarie bigint mostrano costantemente una differenza di throughput in scrittura di 2-5× su carichi di lavoro rappresentativi. La soluzione non è abbandonare gli UUID — è usare il tipo di UUID che si ordina.
Come appare v7
Un UUID v7:
01950d29-4f6f-7234-bf01-a8b3c0d9e102 └─────timestamp_ms──────┘ └──random──┘
Primi 48 bit: timestamp Unix in millisecondi (valido fino al ~10889 d.C.). Successivi 4 bit: versione (sempre 7). Successivi 12 bit: casuale. Poi 2 bit di marcatore variante + 62 bit di casuale. Casuale totale: 74 bit — resistente alle collisioni in modo confortevole.
Due UUID v7 creati nello stesso millisecondo competono sui 74 bit di casuale; la probabilità di collisione all’interno di quel millisecondo è circa 10⁻²² per un sistema con un miliardo di ID. Attraverso i millisecondi, il timestamp rende le collisioni strutturalmente impossibili.
Quando v4 è ancora la scelta giusta
- Token di sessione opachidove non vuoi rivelare l’ordine di creazione. v7 rivela timestamp di creazione con risoluzione al ~millisecondo nel prefisso, il che va bene per le chiavi del database ma è problematico per i token sensibili alla privacy.
- Chiavi API, token di accesso, token di reset della password: l’opacità conta più dell’ordinabilità.
- Identificatori di anonimizzazione: dove osservare il timestamp aiuterebbe un attaccante a correlare.
Quando v7 è chiaramente migliore
- Chiavi primarie del database.
- ID di sistemi distribuiti (ID evento, ID ordine, ID traccia).
- Ovunque trarresti vantaggio dalla possibilità di fare ORDER BY id e ottenere l’ordinamento cronologico gratuitamente.
- Ovunque le query su intervalli di data di creazione necessiterebbero altrimenti di un indice separato su created_at.
Il percorso di migrazione
Nuove tabelle: usa v7 fin dall’inizio. Vecchie tabelle: di solito non vale la pena migrare, ma se stai raggiungendo i limiti di throughput in scrittura su una tabella con chiave UUID, passare a v7 è un cambiamento strutturale utile.
Genera entrambi tramite il nostro generatore UUID, che supporta entrambe le versioni.
Dati numerici
- Lunghezza stringa: entrambi sono 36 caratteri con trattini (32 hex + 4 trattini), 32 char senza trattini, 22 char in base64url, 16 byte grezzi.
- Entropia: v4 ha 122 bit casuali (6 bit consumati dai marcatori di versione + variante); v7 ha 74 bit casuali dopo il timestamp a 48 bit.
- Matematica collisioni v4: probabilità di collisione del 50% dopo aver generato ~2⁶¹ ≈ 2,3 × 10¹⁸ UUID — genera un miliardo al secondo e aspetteresti ~85 anni.
- Intervallo timestamp v7: il campo Unix-millisecondo a 48 bit copre dall’anno 1970 all’anno 10889.
- Benchmark B-tree Postgres (Percona, 2023): ~7.000 inserimenti/sec con v4 vs ~28.000 inserimenti/sec con v7/ULID sullo stesso hardware e schema — un divario di 4× che si allarga man mano che l’indice cresce oltre la RAM.
- Gonfiore dell’indice: una tabella con chiave v4 da 100 M di righe finisce comunemente 2-3× più grande su disco rispetto agli stessi dati con chiave v7 o bigint sequenziale, a causa dello spazio libero lasciato dopo le divisioni di pagina.
- Velocità di generazione:
crypto.randomUUID()(v4) funziona a ~3 M ops/sec in Node 20; le implementazioni v7 (uuid v9, ulid) raggiungono ~2,5 M ops/sec.
Matrice decisionale
| Caso d’uso | Scegli |
|---|---|
| Chiave primaria Postgres / MySQL | v7 (o ULID) |
| ID evento distribuito, ID traccia, ID ordine | v7 |
| Token sessione, token CSRF | v4 (l’opacità conta) |
| Token reset password / magic-link | v4 + TTL breve |
| Prefisso chiave API | v4 |
| Chiave oggetto S3 (listing ordinabile) | v7 |
| Chiave di idempotenza nella consegna di webhook | v7 (ordine cronologico utile per il debug) |
| Slug pubblico | Nessuno dei due — usa NanoID o hash breve |
Fonti
- RFC 9562 — Universally Unique IDentifiers (UUIDs), maggio 2024 — rfc-editor.org/rfc/rfc9562 (definisce v6, v7, v8; sostituisce RFC 4122).
- Percona Blog — UUIDs in Databases: A Deep Dive, benchmark v4 vs chiavi sequenziali — percona.com.
Frequently asked questions
- UUID v7 è retrocompatibile con v4?
- Sì — entrambi sono identificatori a 128 bit nello stesso formato hex 8-4-4-4-12, entrambi si adattano a qualsiasi colonna o API che accetta un UUID v4, ed entrambi codificano la loro versione nello stesso nibble. La migrazione riguarda solo il modo in cui generi i nuovi ID; i v4 esistenti nel database continuano a funzionare senza modifiche.
- v7 rivela il timestamp di creazione?
- Sì — i primi 48 bit sono il timestamp Unix in millisecondi, leggibile da chiunque veda l’UUID. Per la maggior parte delle chiavi del database questo va bene o è addirittura utile, ma per token di sessione opachi o codici di reset della password dove non vuoi esporre l’ordine di creazione, usa v4.
- Perché v4 danneggia le prestazioni del database?
- Perché le chiavi casuali atterrano in pagine casuali di un indice B-tree ad ogni inserimento, causando divisioni di pagina e distruggendo la località della cache. Le chiavi sequenziali (come il prefisso timestamp di v7) si aggiungono alla foglia più a destra e rimangono cache-friendly. I benchmark Postgres e MySQL mostrano tipicamente una differenza di throughput in scrittura di 2-5×.
- Posso generare UUID v7 nel browser?
- Non nativamente ancora — crypto.randomUUID() restituisce v4. Hai bisogno di una piccola libreria (uuid v9+, polyfill basati su ulid) o 10 righe di codice che combinano Date.now() con crypto.getRandomValues(). Il supporto nativo v7 è nella roadmap WHATWG.
Related
Published May 15, 2026