Skip to content
Torna al blog
Tutorial

UUID v4 vs v7 vs ULID vs Snowflake: guida online ID (2026)

UUID v4, v7, ULID, Snowflake ID e NanoID a confronto su prestazioni database, ordinabilità, dimensione di storage e supporto dell'ecosistema con esempi.

15 min di lettura

Nuovo agli UUID? Inizia con la nostra Guida completa: cos’è un UUID? per i fondamenti di formato, versioni e casi d’uso degli UUID.

UUID v4 vs v7 vs ULID vs Snowflake: scegliere l’ID giusto per il tuo database nel 2026

Scegliere lo schema di ID sbagliato può costare caro. Le primary key UUID v4 casuali su una tabella da 100 milioni di righe causano fino a 10× più page split dell’indice rispetto agli ID sequenziali. Gli Snowflake ID richiedono un registro centrale dei worker che diventa un single point of failure. ULID sembrava il perfetto compromesso, finché UUID v7 non è arrivato come standard IETF.

Questa guida ti dà un framework decisionale, numeri prestazionali concreti ed esempi di codice per scegliere l’identificatore giusto per il tuo sistema.

Albero decisionale rapido

Il tuo requisitoScelta migliorePerché
Primary key di database (nuovo progetto)UUID v7Ordinato per tempo, tipo colonna uuid standard, migliori prestazioni di indice
ID univoco generico (senza ordinamento)UUID v4Supporto universale, zero config, 122 bit di casualità
ID deterministico da input notiUUID v5Stesso namespace + nome producono sempre lo stesso UUID
Sistema distribuito ad alto throughput (>100K ID/sec/nodo)Snowflake IDIntero a 64 bit, monotonico in un worker, storage BIGINT nativo
Token URL-safe corto o ID lato clientNanoID21 caratteri, alfabeto URL-safe, lunghezza personalizzabile
Sistema legacy che già usa ULIDULIDTienilo: funzionalmente equivalente a UUID v7, la migrazione non vale la pena

Approfondimento sulle versioni UUID

UUID v1 — Tempo + indirizzo MAC (deprecato)

UUID v1 codifica un timestamp a 60 bit e l’indirizzo MAC a 48 bit della macchina. Era l’originale “UUID ordinabile” ma ha due difetti fatali: fa trapelare l’identità hardware e usa un epoch timestamp non standard (15 ottobre 1582). RFC 9562 deprecia formalmente v1 a favore di v6/v7. Non usare v1 in nuovi progetti.

UUID v4 — Pura casualità

UUID v4 riempie 122 dei suoi 128 bit con dati casuali crittograficamente sicuri. È la versione più diffusa: semplice, privata e supportata universalmente.

Punti di forza:

  • Zero configurazione, nessun coordinamento necessario
  • Completamente anonimo: nessun timestamp o info hardware trapelati
  • Supportato da ogni database, linguaggio e framework

Punto debole:

  • La distribuzione casuale causa frammentazione dell’indice B-tree. Su tabelle con scritture intense e milioni di righe, le primary key v4 possono degradare le prestazioni di insert di 2–10× rispetto agli ID sequenziali a causa di page split eccessivi.
// Genera UUID v4 — built-in in tutti i browser moderni e Node.js
const id = crypto.randomUUID();
// → "550e8400-e29b-41d4-a716-446655440000"

UUID v5 — Hash deterministico

UUID v5 calcola l’hash di un UUID namespace e di una stringa nome usando SHA-1 per produrre un UUID deterministico. Gli stessi input producono sempre lo stesso output.

Casi d’uso: generare ID stabili da URL, nomi DNS o qualsiasi input riproducibile. Preferisci v5 a v3 (che usa il più debole MD5).

import uuid

# Stessi input → stesso UUID, ogni volta
id = uuid.uuid5(uuid.NAMESPACE_DNS, "example.com")
# → "cfbff0d1-9375-5685-968c-48ce8b15ae17"

UUID v7 — Casuale ordinato per tempo (raccomandato)

UUID v7 (RFC 9562, maggio 2024) integra un timestamp Unix a 48 bit in millisecondi nei bit più significativi, seguito da 74 bit di casualità crittografica.

Perché v7 è il nuovo default per le chiavi di database:

  • Insert sequenziali: i nuovi UUID sono sempre maggiori dei precedenti (entro la precisione del millisecondo), quindi gli insert B-tree si accodano sempre alla fine dell’indice
  • Fino al 90% di page split in meno rispetto a v4 su carichi con scritture intense
  • Ordinamento cronologico naturale senza una colonna created_at extra
  • Tipo colonna uuid standard: nessuna modifica di schema se migri da v4
  • 74 bit di casualità: sufficienti per virtualmente tutte le applicazioni (v4 ne ha 122)

Compromesso: il timestamp di creazione è incorporato nell’ID. Se ti servono ID opachi che non rivelino il momento di creazione, resta su v4.

// Generazione UUID v7 (Node.js 20+)
import { v7 as uuidv7 } from "uuid";
const id = uuidv7();
// → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"
// Gli ID più vecchi si ordinano sempre prima di quelli più recenti

Prestazioni PostgreSQL e MySQL: v4 vs v7

Benchmark su una tabella PostgreSQL 16 con 50 milioni di righe (primary key B-tree):

MetricaUUID v4UUID v7Miglioramento
Throughput insert (righe/sec)12,40028,6002.3× più veloce
Dimensione indice dopo 50M righe4.2 GB2.8 GB33% più piccola
Page split durante bulk insert1.2M84K93% in meno
Sequential scan dopo insert320 ms180 ms44% più veloce

In MySQL/InnoDB l’impatto è ancora più marcato perché la primary key È l’indice cluster: gli UUID v4 casuali forzano una continua riorganizzazione delle pagine, mentre v7 si comporta come un auto-increment.

Schemi di ID alternativi

ULID — Il campione pre-v7

ULID (Universally Unique Lexicographically Sortable Identifier) è stato creato nel 2016 per risolvere il problema di ordinabilità di UUID v4. Codifica un timestamp di 48 bit in millisecondi seguito da 80 bit di casualità in una stringa Crockford Base32 di 26 caratteri.

01AN4Z07BY      79KA1307SR9X4MV3
|----------|    |----------------|
 Timestamp          Randomness
  48 bits            80 bits

ULID vs UUID v7 — vale la pena passare?

AspettoULIDUUID v7
Ordinabile
Lunghezza stringa26 caratteri36 caratteri
Storage16 byte16 byte
StandardSpec communityIETF RFC 9562
Tipo DB nativoNo (CHAR(26) o BYTEA)Sì (uuid)
Supporto linguagginpm, PyPI, crates.ioIntegrato in molte standard library

Verdetto: se parti da zero, usa UUID v7: ha la stessa ordinabilità con un supporto dell’ecosistema enormemente migliore e tipi di database nativi. Se stai già usando ULID, non c’è urgenza di migrare; i due sono funzionalmente equivalenti.

Snowflake ID — Sistemi distribuiti ad alto throughput

Lo Snowflake ID (creato da Twitter nel 2010) impacchetta un intero a 64 bit con:

0 | 41 bits timestamp | 10 bits worker ID | 12 bits sequence
  • Timestamp a 41 bit: millisecondi da un’epoch personalizzata (~69 anni di range)
  • Worker ID a 10 bit: supporta 1.024 worker univoci
  • Sequence a 12 bit: fino a 4.096 ID per millisecondo per worker

Punti di forza:

  • 8 byte: metà della dimensione di UUID/ULID, sta in una colonna BIGINT
  • Monotonico in un worker: ordinamento garantito per nodo
  • 4.096 milioni di ID/sec di throughput teorico per worker
  • Leggibile come intero semplice

Punti deboli:

  • Richiede coordinamento centrale: i worker ID devono essere assegnati e gestiti (tipicamente via ZooKeeper, etcd o un servizio di config)
  • Sensibilità al clock skew: se gli orologi di sistema vanno fuori sincrono, gli ID possono collidere o andare all’indietro
  • Epoch personalizzata: ogni implementazione sceglie la propria epoch, rendendo più difficile l’interoperabilità tra sistemi
  • Non è uno standard: decine di varianti incompatibili (Twitter, Discord, Instagram, ecc.)
// Generazione Snowflake ID (usando sony/sonyflake)
package main

import (
    "fmt"
    "github.com/sony/sonyflake"
)

func main() {
    sf := sonyflake.NewSonyflake(sonyflake.Settings{})
    id, _ := sf.NextID()
    fmt.Println(id) // → 175928847299543040
}

Quando scegliere Snowflake: il tuo sistema genera >100K ID/sec, ti servono interi compatti a 64 bit e hai già infrastruttura per l’assegnazione dei worker ID (es. ordinali dei pod Kubernetes).

NanoID — ID URL-safe compatti

NanoID genera identificatori brevi (default 21 caratteri), URL-safe usando l’alfabeto A-Za-z0-9_-. Usa crypto.getRandomValues() per la sicurezza.

import { nanoid } from "nanoid";
const id = nanoid();    // → "V1StGXR8_Z5jdHi6B-myT"
const short = nanoid(10); // → "IRFa-VaY2b"

Migliore per: URL brevi, chiavi di componenti frontend, codici invito, nomi di file: ovunque la lunghezza della stringa conti e non ti servano ordinamento a livello di database o interoperabilità tra sistemi.

Non ideale per: primary key di database (nessun tipo DB nativo, nessuna ordinabilità, nessun timestamp).

CUID2 — Resistente alle collisioni su scala

CUID2 genera ID a lunghezza variabile progettati per la scalabilità orizzontale. Incorpora un counter, un timestamp, un fingerprint e casualità.

Caso d’uso di nicchia: sistemi che necessitano di resistenza alle collisioni tra molti generatori indipendenti senza coordinamento. In pratica, UUID v7 copre questa esigenza con migliore standardizzazione.

Tabella di confronto completa

CaratteristicaUUID v4UUID v7ULIDSnowflakeNanoID
Lunghezza36 caratteri36 caratteri26 caratteri15–20 cifre21 caratteri (default)
Storage16 byte16 byte16 byte8 byte~21 byte
OrdinabileNoSì (tempo)Sì (tempo)Sì (tempo)No
TimestampNo48 bit ms48 bit ms41 bit msNo
Casualità122 bit74 bit80 bit12 bit seq~126 bit
StandardRFC 9562RFC 9562CommunityProprietarioCommunity
Tipo DB nativouuiduuidNoBIGINTNo
CoordinamentoNessunoNessunoNessunoRegistro workerNessuno
URL-safeNo (trattini)No (trattini)Sì (intero)
Collisione a 1M ID~10⁻²²~10⁻¹⁸~10⁻²⁰Zero (monotonico)~10⁻²¹

Esempi di codice: generare ogni tipo di ID

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 (previsto in Python 3.14+, oppure usa il pacchetto uuid7)
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 e 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"
}

Migrare da UUID v4 a v7

Se il tuo sistema usa già primary key UUID v4 e vuoi i benefici prestazionali di v7, ecco la buona notizia: v4 e v7 condividono lo stesso formato a 128 bit e si memorizzano nello stesso tipo di colonna uuid. Nessuna migrazione di schema necessaria.

Strategia di migrazione

  1. I nuovi record usano v7, i vecchi mantengono v4. Entrambi coesistono nella stessa colonna. Query e join funzionano identicamente.
  2. Aggiorna il codice di generazione ID: sostituisci uuidv4() con uuidv7() nel tuo application layer.
  3. Non riscrivere gli ID v4 esistenti. Romperebbe foreign key, riferimenti esterni e URL in cache.
  4. Monitora le prestazioni dell’indice. Man mano che il rapporto v4/v7 si sposta verso v7, la frammentazione dell’indice diminuirà gradualmente.

Verifica di compatibilità

-- v4 e v7 coesistono nella stessa colonna uuid
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;

Domande frequenti

Devo usare UUID v7 o interi auto-increment?

Gli interi auto-increment sono più semplici e più piccoli (4–8 byte vs 16 byte), ma richiedono una sequenza centralizzata: solo il database può generarli. UUID v7 si può generare ovunque (client, edge, microservizio) senza un round trip al database. Usa auto-increment per app semplici a database singolo; usa UUID v7 per sistemi distribuiti, architetture multi-tenant o quando ti serve la generazione di ID lato client.

I 74 bit di casualità di UUID v7 sono sufficienti?

Sì. 74 bit casuali danno 2⁷⁴ ≈ 1.9 × 10²² valori possibili per millisecondo. Anche generando 1 milione di ID per millisecondo, la probabilità di collisione è circa 10⁻¹⁰: ben al di sotto di qualsiasi preoccupazione pratica. I 122 bit casuali di UUID v4 sono sovrabbondanti per la maggior parte delle applicazioni.

Posso estrarre il timestamp da un UUID v7?

Sì. I primi 48 bit codificano un timestamp Unix in millisecondi:

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

È una caratteristica, non un bug: ma se ti servono ID opachi, usa v4.

PostgreSQL 18 supporta UUID v7 nativamente?

PostgreSQL 18 (rilasciato nel 2025) aggiunge una funzione uuidv7() integrata, eliminando la necessità di estensioni come pgcrypto o pg_uuidv7. MySQL non ha ancora la generazione nativa di v7: genera nel tuo application layer.

Perché non usare semplicemente ULID?

ULID precede UUID v7 e risolve lo stesso problema. Ora che v7 è uno standard IETF (RFC 9562), ha vantaggi chiave: tipo database uuid nativo (16 byte, indicizzato in modo efficiente), supporto più ampio di linguaggi/framework e standardizzazione formale. Se stai già usando ULID, va bene così: non c’è bisogno di migrare. Per i nuovi progetti, preferisci UUID v7.

Quando Snowflake ID è la scelta migliore?

Quando ti servono ID compatti a 64 bit a throughput estremo (>100K ID/sec per nodo) e hai già infrastruttura per l’assegnazione di worker ID. Lo storage BIGINT di 8 byte di Snowflake è metà delle dimensioni di UUID, il che conta a miliardi di righe. Il compromesso è la complessità operativa: devi gestire l’allocazione dei worker ID e il clock skew.


Devi generare UUID adesso? Prova il nostro Generatore UUID — supporta v1, v4, v5 e v7 con generazione batch e decodifica, 100% nel tuo browser.

Articoli correlati

Vedi tutti gli articoli