Generatore UUID v1
Genera UUID v1 basati sul tempo con timestamp integrato
…
Formatta
Cos'è UUID v1?
UUID v1 è la versione UUID originale, standardizzata in RFC 4122 (2005). Genera identificatori univoci combinando un timestamp ad alta precisione con l'indirizzo MAC dell'host generatore, più una breve sequenza di clock per gestire la risoluzione sub-timestamp.
Poiché il timestamp è incorporato, i valori UUID v1 dallo stesso host sono monotonicamente crescenti nel tempo — rendendoli naturalmente ordinati. Questo era progettato per sistemi distribuiti dove ogni nodo poteva generare UUID indipendentemente senza coordinazione.
Oggi UUID v1 è ampiamente superato da UUID v7 (ordinabile, nessuna divulgazione MAC) e UUID v4 (completamente casuale, privato). Rimane in uso in sistemi come Apache Cassandra e database distribuiti legacy.
Anatomia di un UUID v1
Una stringa UUID v1 come 550e8400-e29b-11d4-a716-446655440000 codifica sei campi distinti:
| Campo | Dimensione | Descrizione |
|---|---|---|
| time_low | 32 bits | Campo basso a 32 bit del timestamp gregoriano a 60 bit (intervalli di 100 nanosecondi dal 15 ott. 1582) |
| time_mid | 16 bits | Campo medio a 16 bit del timestamp a 60 bit |
| time_hi_and_version | 16 bits | I 12 bit superiori del timestamp a 60 bit più il numero di versione a 4 bit (sempre <code>1</code>) |
| clock_seq_hi_res | 8 bits | Campo alto della sequenza di clock a 6 bit combinato con il marcatore di variante RFC 4122 a 2 bit |
| clock_seq_low | 8 bits | 8 bit inferiori della sequenza di clock |
| node | 48 bits | Identificatore di nodo a 48 bit — tipicamente l'indirizzo MAC dell'interfaccia di rete generatrice, o un valore casuale a 48 bit se nessun MAC è disponibile |
Il campo della sequenza di clock (clock_seq_hi_res + clock_seq_low) è un contatore a 14 bit. Viene incrementato ogni volta che il clock di sistema va indietro (es. regolazione NTP) o quando il sistema si riavvia senza persistere l'ultimo timestamp noto. Questo impedisce la generazione di UUID duplicati se il clock non avanza monotonicamente.
Decodifica del timestamp UUID v1
Il timestamp a 60 bit è distribuito su tre campi nell'UUID. Per ricostruire il tempo di generazione:
- Estrarre
time_low(byte 0-3),time_mid(byte 4-5) etime_hi(byte 6-7, meno il nibble di versione) - Riassemblare:
(time_hi << 48) | (time_mid << 32) | time_low - Il risultato è un conteggio a 60 bit di
intervalli di 100 nanosecondidal 15 ottobre 1582 (l'epoch del calendario gregoriano) - Sottrarre l'offset gregoriano-verso-Unix: 122.192.928.000.000.000 (intervalli di 100 ns tra il 15 ott. 1582 e il 1 gen. 1970)
- Dividere per
10.000per convertire gli intervalli di 100 nanosecondi in millisecondi - Usare il risultato come
timestamp Unix in millisecondiper costruire un oggetto Date - Formattare in
ISO 8601per output leggibile dall'uomo
La precisione del timestamp è 100 nanosecondi — molto più fine della precisione in millisecondi di UUID v7. Tuttavia, in pratica la maggior parte dei sistemi operativi non espone una risoluzione dell'orologio sub-millisecondo, quindi i bit inferiori sono spesso a zero o sintetizzati.
Problemi di privacy
Lo svantaggio più significativo di UUID v1 è che incorpora l'indirizzo MAC dell'host generatore nel campo node. Ciò significa che ogni UUID v1 porta un'impronta permanente e globalmente univoca della macchina che lo ha generato.
Un avversario che ottiene un UUID v1 può determinare: (1) il tempo approssimativo in cui l'ID è stato generato, (2) l'indirizzo MAC dell'host generatore, e (3) analizzando più UUID, il tasso al quale gli ID vengono generati.
Per questa ragione, UUID v1 non dovrebbe mai essere usato come identificatore pubblico (es. in URL o risposte API) a meno che non si sia a proprio agio nel divulgare queste informazioni. RFC 4122 stesso nota che un sistema può usare un valore casuale a 48 bit invece dell'indirizzo MAC, ma molte implementazioni non lo fanno.
Quando UUID v1 è ancora appropriato
UUID v1 vs UUID v7
UUID v7 è il successore moderno di UUID v1 per gli identificatori ordinati nel tempo. Ecco un confronto diretto:
| Aspetto | UUID v1 | UUID v7 |
|---|---|---|
| Epoch / Base temporale | Epoch gregoriano (15 ott. 1582) | Epoch Unix (1 gen. 1970) |
| Precisione | 100 nanosecondi | 1 millisecondo |
| Identificatore nodo | Indirizzo MAC (divulga l'identità dell'host) | Casuale (privato) |
| Privacy | Divulga indirizzo MAC e timestamp di generazione | Nessuna informazione sull'host incorporata |
| Prestazioni indice DB | Buone — sequenziale per host | Eccellenti — k-sortable su tutti i generatori |
| Standard | RFC 4122 (2005) | RFC 9562 (2024) |
Per i nuovi progetti, UUID v7 è il sostituto raccomandato per UUID v1. Fornisce garanzie simili di ordinamento temporale senza le implicazioni sulla privacy legate all'incorporare l'indirizzo MAC dell'host.
Esempi di codice
La generazione UUID v1 non è disponibile nativamente nei browser o Node.js. Usare il pacchetto npm uuid:
// Generate a UUID v1 using the Web Crypto API
function generateUuidV1() {
const buf = new Uint8Array(16)
crypto.getRandomValues(buf)
const ms = BigInt(Date.now())
const gregorianOffset = 122192928000000000n
const t = ms * 10000n + gregorianOffset
const tLow = Number(t & 0xFFFFFFFFn)
const tMid = Number((t >> 32n) & 0xFFFFn)
const tHiVer = Number((t >> 48n) & 0x0FFFn) | 0x1000 // version 1
const clockSeq = (buf[8] & 0x3F) | 0x80 // variant 10xxxxxx
const clockSeqLow = buf[9]
const hex = (n, pad) => n.toString(16).padStart(pad, '0')
const node = [...buf.slice(10)].map(b => b.toString(16).padStart(2, '0')).join('')
return `${hex(tLow,8)}-${hex(tMid,4)}-${hex(tHiVer,4)}-${hex(clockSeq,2)}${hex(clockSeqLow,2)}-${node}`
}
// Extract the embedded timestamp from a UUID v1
function extractTimestamp(uuid) {
const parts = uuid.split('-')
const tHex = parts[2].slice(1) + parts[1] + parts[0]
const t = BigInt('0x' + tHex)
const ms = (t - 122192928000000000n) / 10000n
return new Date(Number(ms))
}
const id = generateUuidV1()
console.log(id) // e.g. "1eb5e8b0-6b4d-11ee-9c45-a1f2b3c4d5e6"
console.log(extractTimestamp(id)) // e.g. 2023-10-15T12:34:56.789Zimport uuid from datetime import datetime, timezone # Generate UUID v1 (uses MAC address by default) uid = uuid.uuid1() print(uid) # Extract embedded timestamp # uuid.time is 100-ns intervals since Oct 15, 1582 GREGORIAN_OFFSET = 122192928000000000 # 100-ns intervals ts_100ns = uid.time ts_ms = (ts_100ns - GREGORIAN_OFFSET) // 10000 dt = datetime.fromtimestamp(ts_ms / 1000, tz=timezone.utc) print(dt.isoformat()) # e.g. "2023-10-15T12:34:56.789000+00:00"
package main
import (
"fmt"
"time"
"github.com/google/uuid" // go get github.com/google/uuid
)
func main() {
id, _ := uuid.NewUUID() // UUID v1
fmt.Println(id)
// Extract timestamp from UUID v1
// uuid.Time is 100-ns ticks since Oct 15, 1582
t := id.Time()
sec := int64(t)/1e7 - 12219292800 // convert to Unix seconds
nsec := (int64(t) % 1e7) * 100
ts := time.Unix(sec, nsec).UTC()
fmt.Println(ts.Format(time.RFC3339Nano))
}