Generatore di UUID

Genera UUID uno alla volta o cinquanta: v4 casuali, oppure v7 ordinati nel tempo, il cui timestamp ordina un lotto e ne fa una chiave di database migliore.

Versione

122 bit casuali. Non rivela nulla su quando è stato creato.

Quanti

Generazione…

Che cos’è un UUID e perché esiste

Un UUID — Universally Unique Identifier, chiamato GUID nella documentazione Microsoft — è un valore da 128 bit scritto come 32 cifre esadecimali nel familiare raggruppamento 8-4-4-4-12. Il suo scopo è permettere a sistemi distinti di coniare identificatori in modo indipendente, senza alcun coordinamento, restando comunque ragionevolmente certi che i risultati non collideranno. È questo a distinguerlo da una colonna auto-incrementale: due server, due client mobili offline su un aereo e un processo in background possono creare record nello stesso istante senza chiedere il permesso a nessuno.

L’unicità qui è probabilistica, non garantita. La versione 4 lascia 122 bit alla casualità, uno spazio abbastanza ampio perché, anche generando miliardi di valori, la probabilità di una ripetizione resti molto al di sotto di quella che un disco corrompa i dati in silenzio. In pratica puoi trattarlo come unico: la matematica non è l’anello debole.

Versione 4 contro versione 7: la scelta che conta

La versione 4 è casuale da cima a fondo. Non trasporta alcuna informazione: né quando è stata creata, né da chi, né in quale ordine. È il suo pregio maggiore o il suo difetto centrale, a seconda di dove la collochi.

La versione 7, standardizzata nell’RFC 9562 nel 2024, sostituisce i primi 48 bit con un timestamp Unix in millisecondi e riempie il resto di casualità. Poiché il tempo viene per primo e il valore si legge da sinistra a destra, ordinare identificatori v7 come semplice testo li ordina anche cronologicamente.

Non è una differenza estetica. I database tengono le chiavi primarie in un indice B-tree ordinato. Inserisci chiavi v7 e ogni nuova riga atterra sul bordo destro dell’albero, accanto alla precedente: le pagine su cui scrivi restano in memoria e l’indice cresce ordinato. Inserisci chiavi v4 e ogni scrittura cade in una posizione casuale, il database tocca ogni volta una pagina diversa, il tasso di successo della cache cala e l’indice si frammenta. Su una tabella grande e trafficata la differenza di throughput in inserimento è notevole, ed è per questo che v7 è stato adottato così rapidamente per le chiavi primarie.

  • Scegli v7 per chiavi primarie, identificatori di eventi e log, e tutto ciò che vorrai ordinare o interrogare per intervallo di data di creazione.
  • Scegli v4 quando l’identificatore compare in un contesto non fidato e non deve trapelare nulla — nemmeno che un record è stato creato appena prima di un altro.
  • Entrambi vanno bene per un ID di correlazione, una chiave di idempotenza o un nome di file, dove l’ordine non conta.

Il compromesso è esattamente quella fuga di informazione: un identificatore v7 dice a chiunque lo veda, al millisecondo, quando è stato creato. Per l’ID di una riga è di solito innocuo e spesso utile. Per un token di reimpostazione password o un link di condivisione pubblico è un’informazione che non intendevi pubblicare — e quelli non dovrebbero essere UUID affatto, ma token casuali da un generatore di segreti dedicato.

L’ordine all’interno dello stesso millisecondo

Una sottigliezza che coglie in fallo molte implementazioni di v7: l’hardware moderno genera ben più di un identificatore al millisecondo. Se due valori condividono il timestamp, il loro ordine è deciso dai bit casuali che seguono, cioè a caso. Generane cinquanta in un ciclo stretto e otterrai cinquanta valori solo approssimativamente ordinati, perdendo proprio la proprietà per cui hai scelto v7.

L’RFC 9562 risolve la cosa con un contatore monotono, e questo strumento lo implementa: i 12 bit immediatamente successivi al timestamp contano in avanti all’interno di un millisecondo, così un lotto è rigorosamente crescente e non soltanto approssimativo. Se il contatore si riempie — più di 4096 valori nello stesso millisecondo — il generatore prende in prestito il millisecondo successivo anziché azzerarsi ed emettere un valore che si ordini prima del precedente. L’orologio che torna indietro, cosa che accade con le correzioni NTP, è gestito allo stesso modo.

Leggendo un valore v7 da sinistra a destra, prendendo 0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b come esempio:

  • 0190a1b2-c3d4 — il timestamp Unix a 48 bit in millisecondi. Venendo per primo, l’ordine del testo è l’ordine del tempo.
  • 7 — la cifra di versione, ciò che rende questo valore un v7 e non un v4.
  • e5f — il contatore monotono a 12 bit, che si incrementa all’interno di un singolo millisecondo.
  • 8 — i bit di variante, fissati dall’RFC 9562 per ogni UUID moderno (sempre 8, 9, a o b).
  • a9b-0c1d2e3f4a5b — i restanti 62 bit, puramente casuali.

Memorizzare e usare gli UUID

La forma canonica è minuscola con trattini, e l’RFC 9562 dice che i generatori devono emettere esattamente quella — motivo per cui questo strumento non offre opzioni di formato. Incontrerai comunque altre grafie: maiuscole e tra parentesi graffe negli strumenti Microsoft, e senza trattini dove qualcuno voleva una colonna più corta. Sono gli stessi 128 bit, e i confronti dovrebbero ignorare maiuscole e minuscole.

Dove conta davvero è la memorizzazione. Un UUID occupa 16 byte, ma la sua forma testuale ne occupa 36: memorizzarlo come stringa più che raddoppia lo spazio nella riga e, cosa più importante, in ogni indice che lo include. Usa un tipo nativo dove esiste:

uuid                      -- PostgreSQL: tipo nativo da 16 byte
BINARY(16)                -- MySQL: compatto; CHAR(36) spreca 20 byte per riga
uniqueidentifier          -- SQL Server
crypto.randomUUID()       // JavaScript: solo v4, richiede un contesto sicuro
uuid.uuid4() / uuid7()    # Python: v4 nella libreria standard; v7 con una libreria

Un’ultima avvertenza: un UUID identifica, non autorizza. Poiché non sono indovinabili, viene la tentazione di trattare come privato un URL non elencato che ne contiene uno. Ma gli identificatori trapelano — attraverso log, cronologia del browser, header referrer e screenshot — quindi tutto ciò che ha davvero bisogno di protezione richiede comunque un vero controllo dei permessi alle spalle.

Come capire se un UUID è un v4 o un v7

Di solito un identificatore ti arriva senza alcuna nota che dica da dove viene, e non serve: la versione è scritta dentro il valore stesso. Entrambi sono 32 cifre esadecimali nello stesso raggruppamento 8-4-4-4-12, quindi la differenza non sta nella forma — sta in due singole cifre in posizioni fisse, e tutto il resto ne discende.

  • La prima cifra del terzo gruppo è la cifra di versione. Un 4 lì significa versione 4, un 7 significa versione 7, e nessun’altra parte del valore ha voce in capitolo.
  • La prima cifra del quarto gruppo porta i bit di variante ed è 8, 9, a o b in entrambe le versioni: non ti dirà quindi mai quale hai in mano. Quello che ti dice è che il valore segue l’RFC 9562; qualunque altra cosa in quella posizione è una disposizione più vecchia, o non è un UUID.
  • Se la cifra di versione è un 7, le prime dodici cifre sono l’ora di creazione: un conteggio a 48 bit dei millisecondi dall’inizio del 1970, scritto in esadecimale.
  • Se è un 4, non c’è altro da leggere. Un v4 non codifica né ora, né macchina, né ordine, che è esattamente la proprietà per cui lo hai scelto.

Prendi il valore che la sezione precedente scompone, 0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b. Il suo terzo gruppo si apre con un 7, quindi è una versione 7, e le sue prime dodici cifre sono 0190a1b2c3d4: converti quel numero esadecimale in decimale, consegnalo al Convertitore di timestamp Unix e ottieni un pomeriggio di luglio 2024. Accanto, 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d apre il suo terzo gruppo con un 4, e le sue prime dodici cifre sono dati casuali che non decodificano nulla. I due concordano esattamente su una cosa: entrambi aprono il quarto gruppo con una delle quattro cifre che i bit di variante consentono.

Il v7 resiste alle collisioni quanto il v4?

È un dubbio legittimo, perché il v7 porta davvero meno casualità. La versione 4 spende 122 dei suoi 128 bit in dati casuali. La versione 7 ne spende 48 per il timestamp e, qui, altri 12 per il contatore monotono, lasciando 62 bit casuali: poco più della metà. Letto come numero nudo sembra un arretramento serio.

  • All’interno di un singolo lotto prodotto da questo strumento una ripetizione non è improbabile, è impossibile. I valori che condividono un millisecondo ricevono valori di contatore diversi, e quelli che non lo condividono portano timestamp diversi, quindi non possono essercene due uguali: è aritmetica, non probabilità.
  • Tra due macchine che generano nello stesso istante, le probabilità sono al più il problema dei compleanni su 62 bit, che va in pari all’incirca alla radice quadrata dello spazio: nell’ordine di due miliardi di valori, e tutti in un unico stesso millisecondo.
  • Tra millisecondi diversi una collisione di v7 non è solo improbabile ma impossibile, perché differiscono già le cifre iniziali.

Il confronto onesto non è dunque 122 bit contro 62. È una lotteria estratta più e più volte per tutta la vita del sistema contro una lotteria separata e molto più piccola tenuta dentro ogni millisecondo e buttata via quando il millisecondo finisce. Il secondo assetto è il più forte, e il motivo per preferire il v4 resta quello che dà la sezione del confronto: non le collisioni, ma il fatto che un v7 dica ad alta voce quando è stato creato.

Passare una tabella esistente da v4 a v7

La domanda che segue la scelta del v7 è cosa fare delle righe già presenti nella tabella, e la parte rassicurante è che la colonna non deve cambiare affatto. Entrambe le versioni sono gli stessi 128 bit nella stessa forma testuale da 36 caratteri, quindi una colonna uuid di PostgreSQL, un BINARY(16) o un CHAR(36) reggono un misto senza accorgersene. Inizi a generare v7 per le righe nuove e ti fermi lì: non c’è passo di migrazione né riempimento a ritroso.

  • Quello che arriva subito: ogni riga inserita d’ora in poi porta un timestamp in testa, quindi le chiavi nuove atterrano una accanto all’altra a un’estremità dell’indice invece di sparpagliarsi su di esso. Questo vantaggio riguarda dove vanno le scritture nuove, ed è lì dal primo inserimento.
  • Quello che non arriva mai: le righe già presenti restano senza ordine per sempre. Nulla può mettere un’ora di creazione in un valore a cui non è mai stata data, e riemettere ogni identificatore significa riscrivere ogni chiave esterna che punta a esso — un lavoro molto più grande che cambiare generatore, e raramente ne vale la pena per il solo indice.
  • A cosa fare attenzione: ordinare la colonna smette di voler dire una cosa sola. Un conteggio di millisecondi è un numero piccolo per un campo da 48 bit, quindi le chiavi v7 si raccolgono in una banda stretta nella parte bassa dell’intervallo mentre quelle v4 sono sparse su tutto l’intervallo, e ogni tanto una di esse ci finisce in mezzo.

È quest’ultimo punto a mordere, perché una query che ordina per chiave sembra corretta sui dati nuovi e riferisce in silenzio male sui vecchi. Se ti serve un unico ordine su tutta la tabella, aggiungi una colonna con l’ora di creazione e ordina su quella, e lascia che l’identificatore torni a essere un identificatore. Il misto almeno resta leggibile nel frattempo: la cifra di versione sta in ogni valore, quindi una query può distinguere le due epoche senza una seconda colonna quando le serve.

Domande frequenti

Meglio v4 o v7?
Usa v7 per le chiavi primarie e per tutto ciò che ordinerai per data di creazione: il timestamp iniziale tiene gli inserimenti raggruppati in fondo all’indice invece che sparsi. Usa v4 quando l’identificatore non deve rivelare nulla, nemmeno quando è stato creato.
Due UUID possono essere uguali?
È possibile ma incredibilmente improbabile. La versione 4 ha 122 bit casuali, quindi anche dopo miliardi di valori generati la probabilità di collisione resta molto inferiore a quella che la memoria li corrompa in silenzio.
Che fine hanno fatto le versioni 1, 3 e 5?
La versione 1 codifica un timestamp e l’indirizzo MAC della macchina, rivelando l’identità dell’hardware e comunque non producibile in un browser. Le versioni 3 e 5 derivano un UUID in modo deterministico da uno spazio dei nomi e un nome, usando MD5 o SHA-1: utile quando lo stesso input deve sempre dare lo stesso identificatore, ma è un lavoro diverso dal generarne uno nuovo.
Un UUID è abbastanza sicuro come token segreto?
Un UUID v4 non è indovinabile, ma uno v7 codifica apertamente il proprio istante di creazione, e nessuno dei due è pensato come credenziale. Per reimpostazioni di password, token di sessione o link di condivisione, genera un segreto casuale dedicato e verifica i permessi sul server invece di affidarti alla difficoltà di indovinare l’identificatore.
Perché il mio lotto di v7 non è perfettamente ordinato in altri strumenti?
Perché molte implementazioni saltano il contatore monotono. Quando più valori condividono un millisecondo, il loro ordine è lasciato ai bit casuali che seguono. Questo strumento implementa il contatore dell’RFC 9562, quindi un lotto generato qui è rigorosamente crescente.
Come dovrei memorizzare un UUID in un database?
In un tipo nativo da 16 byte dove esiste — uuid in PostgreSQL, uniqueidentifier in SQL Server, BINARY(16) in MySQL. Memorizzare invece la forma testuale da 36 caratteri più che raddoppia lo spazio usato nella riga e in ogni indice che include la colonna.
Vengono generati sui vostri server?
No. Vengono generati nel tuo browser con crypto.getRandomValues, la sorgente crittografica di casualità della piattaforma — la stessa usata da crypto.randomUUID. Nessun valore viene inviato da nessuna parte, e ricaricare la pagina produce un insieme completamente nuovo.
Si può sapere quando è stato creato un UUID v7?
Sì, e non serve altro che il valore. Le sue prime dodici cifre esadecimali sono un conteggio di millisecondi dall’inizio del 1970: convertile in decimale e consegna il risultato al Convertitore di timestamp Unix. Un v4 non ha quel campo, quindi lì le stesse dodici cifre sono casuali e non decodificano nulla.
Il v7 ha meno bit casuali del v4?
Sì: 62 qui contro i 122 del v4, perché il timestamp e il contatore prendono lo spazio. In pratica non rende più probabile una collisione: due valori v7 possono scontrarsi solo se sono stati creati nello stesso millisecondo, quindi quei 62 bit si spendono dentro un millisecondo e non lungo l’intera vita del sistema, e dentro un singolo lotto di questo strumento il contatore rende una ripetizione impossibile invece che soltanto improbabile.
UUID v4 e v7 possono stare nella stessa colonna?
Sì. Sono gli stessi 128 bit nella stessa forma testuale, quindi nello schema non cambia nulla e non c’è migrazione: generi v7 da adesso e lasci in pace le righe vecchie. L’unica cosa da sorvegliare è l’ordinamento: le righe nuove stanno fra loro in ordine di creazione, ma quelle vecchie e casuali sono sparse intorno, quindi ordinare per chiave non è un ordine temporale per la tabella nel suo insieme.
Che cosa sono le versioni 6 e 8 di UUID?
L’RFC 9562 le definisce entrambe accanto al v7. La versione 6 è la versione 1 con i campi del timestamp riordinati perché il valore si ordini cronologicamente, ed è pensata per sistemi già legati al v1: l’RFC dice che tutto il resto dovrebbe usare il v7. La versione 8 è uno spazio deliberatamente aperto per disposizioni proprie, in cui sono fissati solo i bit di versione e di variante e i restanti 122 li definisci tu. Questo strumento non genera né l’una né l’altra.

Strumenti correlati