Generatore UUID v7
Genera UUID v7 ordinati nel tempo per chiavi primarie di database
…
Formatta
Cos'è UUID v7?
UUID v7 è un formato UUID di nuova generazione standardizzato in RFC 9562 (maggio 2024). Codifica un timestamp Unix a 48 bit in millisecondi nei bit più significativi, seguito da marcatori di versione e variante, e riempie i bit rimanenti con dati casuali crittograficamente sicuri.
Poiché il timestamp occupa i bit più significativi, i valori UUID v7 si ordinano cronologicamente — sia lessicograficamente che numericamente. Questo li rende un'ottima scelta per le chiavi primarie dei database, dove gli UUID casuali (v4) causano frammentazione degli indici B-tree.
Perché gli UUID casuali frammentano gli indici dei database
Gli indici B-tree — usati da PostgreSQL, MySQL, SQLite e dalla maggior parte degli altri database — mantengono le righe ordinate per valore di chiave. Quando si inserisce una nuova riga, il database deve posizionarla nella posizione ordinata corretta all'interno dell'albero degli indici.
Con UUID v4 (completamente casuale), ogni nuovo inserimento attertra in una posizione essenzialmente casuale nell'albero degli indici. Questo costringe il database a leggere e riscrivere costantemente le pagine degli indici interni, dividendo le pagine piene e lasciandone altre a metà vuote. Il risultato è un indice frammentato e gonfio che rallenta sia le scritture che le letture man mano che la tabella cresce.
Layout dei bit di UUID v7
UUID v7 è largo 128 bit, diviso in sei campi:
| Bit | Campo | Scopo |
|---|---|---|
| 48 | unix_ts_ms | Timestamp Unix a 48 bit in millisecondi — occupa l'intera metà superiore |
| 4 | ver | Numero di versione — sempre 0111 (decimale 7) |
| 12 | rand_a | 12 bit di dati casuali crittograficamente sicuri |
| 2 | var | Marcatore di variante — sempre 10 (variante RFC 4122) |
| 62 | rand_b | 62 bit di dati casuali crittograficamente sicuri |
La precisione del timestamp è 1 millisecondo. All'interno dello stesso millisecondo, i valori UUID v7 sono ordinati dal loro suffisso casuale — non è garantito che siano monotonicamente crescenti sotto il millisecondo, ma sono k-sortable: gli ID generati vicini nel tempo si ordineranno vicini nell'indice.
UUID v7 vs UUID v1
Sia UUID v1 che UUID v7 incorporano un timestamp, ma differiscono significativamente nel design:
| Funzionalità | UUID v7 | UUID v1 |
|---|---|---|
| Epoch / Base temporale | Epoch Unix (1 gen. 1970) | Epoch gregoriano (15 ott. 1582) |
| Precisione temporale | 1 millisecondo | 100 nanosecondi |
| Ordinabile | Sì — k-sortable per design | No — i campi temporali sono mescolati nel layout UUID |
| Privacy | Nessuna informazione sull'host divulgata | Incorpora l'indirizzo MAC dell'host generatore |
| Prestazioni indice DB | Eccellenti — inserimenti sequenziali, frammentazione minima | Scarse — non sequenziale nonostante il timestamp |
| Standard | RFC 9562 (2024) | RFC 4122 (2005) |
| Supporto nativo browser | Non ancora (nessun crypto.randomUUID v7) | Non disponibile nativamente |
Per qualsiasi nuovo progetto che necessita di UUID ordinati nel tempo, preferire UUID v7 a UUID v1. UUID v1 è legacy e divulga informazioni sull'host.
UUID v7 vs ULID
ULID (Universally Unique Lexicographically Sortable Identifier) risolve un problema simile a UUID v7. Ecco come si confrontano:
- Segue lo standard UUID RFC 9562 — compatibile con tutti gli strumenti UUID
- Formato esadecimale con trattini — universalmente riconosciuto
- Supporto nativo per colonne UUID nei database
- 128 bit totali
- Codifica Crockford Base32 — 26 caratteri, leggermente più compatto
- Case-insensitive ed evita caratteri ambigui (I, L, O, U)
- Più leggibile dall'uomo a colpo d'occhio
- Richiede una libreria — nessun supporto nativo della piattaforma
Se si è già in un ecosistema UUID (colonna uuid PostgreSQL, API REST che restituiscono UUID), usare UUID v7. Se si parte da zero e si preferisce una codifica più user-friendly, ULID è un'alternativa ragionevole.
Usare UUID v7 nei database
UUID v7 non è ancora generato nativamente dalla maggior parte dei database, ma può essere memorizzato in colonne UUID standard e generato nel codice applicativo o tramite estensioni:
uuid. L'estensione pg-uuidv7 aggiunge una funzione lato server uuid_generate_v7() se si hanno bisogno di ID generati dal DB.BINARY(16) o CHAR(36). Generare nel codice applicativo. MySQL 8.0+ ha supporto UUID ordinato tramite UUID_TO_BIN(UUID(), 1) per v1, ma v7 richiede generazione a livello applicativo.TEXT (36 chars) o BLOB (16 byte). Generare nel codice applicativo. L'ordinamento lessicografico su TEXT funziona correttamente perché UUID v7 usa un prefisso timestamp a larghezza fissa.Esempi di codice
UUID v7 non è ancora disponibile tramite crypto.randomUUID(). Usare una libreria come uuidv7 (npm) finché non arriva il supporto nativo:
function generateUuidV7() {
const buf = new Uint8Array(16)
crypto.getRandomValues(buf)
const ms = BigInt(Date.now())
// Embed 48-bit Unix ms timestamp
buf[0] = Number((ms >> 40n) & 0xFFn)
buf[1] = Number((ms >> 32n) & 0xFFn)
buf[2] = Number((ms >> 24n) & 0xFFn)
buf[3] = Number((ms >> 16n) & 0xFFn)
buf[4] = Number((ms >> 8n) & 0xFFn)
buf[5] = Number(ms & 0xFFn)
// Set version 7 (0111xxxx)
buf[6] = (buf[6] & 0x0F) | 0x70
// Set variant (10xxxxxx)
buf[8] = (buf[8] & 0x3F) | 0x80
const hex = [...buf].map(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)}`
}
// Node.js 20+ built-in
// import { randomUUID } from 'node:crypto' // v4 only — no v7 yet in stdlib# pip install uuid7 import uuid_extensions uid = uuid_extensions.uuid7() print(uid) # e.g. 018e2b3d-1a2b-7000-8000-abc123456789 print(uid.time) # Unix ms timestamp embedded in the UUID # Or as a plain string from uuid_extensions import uuid7str print(uuid7str())
-- PostgreSQL 13+ extension-free implementation
CREATE OR REPLACE FUNCTION uuid_generate_v7()
RETURNS uuid
LANGUAGE sql
AS $$
SELECT encode(
set_bit(
set_bit(
overlay(
uuid_send(gen_random_uuid())
PLACING substring(int8send(floor(extract(epoch FROM clock_timestamp()) * 1000)::bigint) FROM 3)
FROM 1 FOR 6
),
52, 1
),
53, 1
),
'hex'
)::uuid;
$$;
-- Usage as a default primary key
CREATE TABLE events (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7(),
payload jsonb,
created_at timestamptz DEFAULT now()
);function extractTimestamp(uuid: string): Date {
const hex = uuid.replace(/-/g, '')
const ms = parseInt(hex.slice(0, 12), 16) // first 48 bits = ms timestamp
return new Date(ms)
}
const uid = '018e2b3d-1a2b-7000-8000-abc123456789'
console.log(extractTimestamp(uid).toISOString())
// → "2024-03-15T10:22:05.259Z"