Generatore ULID

Genera identificatori unici ordinabili lessicograficamente

Quantità

Clicca su Genera per creare ULID

Cos'è un ULID?

ULID (Universally Unique Lexicographically Sortable Identifier) è un formato identificatore a 128 bit progettato per essere sia univoco che naturalmente ordinabile. A differenza di UUID v4, che è completamente casuale, un ULID incorpora un timestamp Unix di precisione in millisecondi nei suoi bit più significativi — garantendo che gli ID generati successivamente si ordinino dopo gli ID generati in precedenza.

Gli ULID sono codificati usando l'alfabeto Crockford Base32, producendo una stringa compatta di 26 caratteri che è URL-safe, case-insensitive e priva di caratteri visivamente ambigui.

La specifica ULID è stata creata da Alizain Feerasta ed è ampiamente usata in sistemi distribuiti, event sourcing e chiavi primarie di database dove contano sia l'unicità che l'ordinamento temporale. Gli ULID non sono uno standard IETF — sono una specifica comunitaria disponibile su ulid.github.io.

Struttura ULID

Un ULID è largo 128 bit, diviso in due componenti:

TimestampCasuale
01ARZ3NDEKTSVE4RRFFQ69G5FAV
48 bit — timestamp Unix in millisecondi. Codifica il tempo di generazione con precisione di 1 ms. Copre date fino all'anno 10889.80 bit — dati casuali crittograficamente sicuri. Rigenerati per ogni ULID (a meno che non si usi la modalità monotona).

Codificato come 26 caratteri Crockford Base32: i primi 10 caratteri rappresentano il timestamp, gli ultimi 16 caratteri rappresentano il componente casuale.

Alfabeto Crockford Base32

Gli ULID usano la codifica Crockford Base32, che usa 32 dei 36 caratteri alfanumerici. L'alfabeto è: 0123456789ABCDEFGHJKMNPQRSTVWXYZ

Perché questi 32 caratteri?
0123456789ABCDEFGHJKMNPQRSTVWXYZ

Quattro caratteri sono intenzionalmente esclusi:

  • I e L sono esclusi — facilmente confusi con la cifra 1
  • O è escluso — facilmente confuso con la cifra 0
  • U è escluso — evita parole oscene accidentali negli ID generati
  • La codifica è case-insensitive01ARZ3NDEKTSV4RRFFQ69G5FAV e 01arz3ndektsv4rrffq69g5fav sono lo stesso ULID

Crockford Base32 è più efficiente dell'esadecimale (32 simboli vs 16) e più leggibile dall'uomo rispetto a Base64 (nessun carattere + / =, case-insensitive).

ULID vs UUID

ULID e UUID rappresentano entrambi identificatori a 128 bit, ma differiscono significativamente nella codifica e negli obiettivi di design:

ProprietàULIDUUID
FormatoCrockford Base32Esadecimale con trattini
Lunghezza26 caratteri36 caratteri
TimestampUnix ms a 48 bitNessuno (v4) o ms a 48 bit (v7)
OrdinabileSì — lessicograficoNo (v4) / Sì (v7)
URL-safeSì (solo alfanumerico)Sì (con trattini)
DipendenzeRichiede libreriaNativo (crypto.randomUUID)
Supporto DBMemorizzare come CHAR(26) o BINARY(16)Tipo colonna UUID nativo
SpecSpec comunitaria (ulid.github.io)RFC 4122 / RFC 9562

Se si è già in un ecosistema UUID (colonne uuid PostgreSQL, API REST, ORM con supporto UUID), UUID v7 è solitamente più adatto di ULID. Se si preferisce una codifica più user-friendly e si controlla lo stack completo, ULID è un'ottima scelta.

ULID vs nanoid

Sia ULID che nanoid producono identificatori brevi e URL-safe, ma hanno obiettivi di design diversi:

ProprietàULIDNanoID
TimestampSì — Unix ms a 48 bitNo
OrdinabileNo
Lunghezza predefinita26 caratteri21 caratteri
Entropia80 bit (componente casuale)~126 bit
AlfabetoCrockford Base32 (32 chars)URL-safe Base64 (64 chars)
Lunghezza personalizzabileNoSì (qualsiasi lunghezza)
Caso d'usoID ordinati nel tempo, chiavi primarie DBToken casuali, URL brevi, chiavi API

Scegliere ULID quando si ha bisogno di ordinamento temporale. Scegliere nanoid quando si ha bisogno di entropia massima in una stringa breve e casuale.

Usare gli ULID nei database

Gli ULID possono essere memorizzati nei database in diversi modi a seconda dei requisiti:

Memorizzare come CHAR(26)
L'approccio più semplice. L'ordinamento lessicografico è preservato. Leggermente più grande dello storage binario ma leggibile dall'uomo e facile da debuggare.
Memorizzare come BINARY(16)
Decodificare l'ULID nella sua rappresentazione binaria a 16 byte per uno storage compatto. Richiede codifica/decodifica nel codice applicativo. L'ordinamento è preservato.
PostgreSQL
Memorizzare come CHAR(26) o usare il tipo bytea per lo storage binario. Non esiste un tipo ULID nativo, ma l'ordinamento lessicografico su CHAR(26) funziona correttamente.
MySQL / MariaDB
Usare CHAR(26) CHARACTER SET ascii per uno storage efficiente. Lo storage binario in BINARY(16) funziona anche e risparmia spazio.
SQLite
Memorizzare come TEXT. Il confronto testo predefinito di SQLite ordina gli ULID correttamente grazie al loro design lessicografico.
MongoDB
Memorizzare come campo stringa. L'ordinabilità lessicografica di ULID funziona naturalmente con il confronto stringhe di MongoDB.

Esempi di codice

La generazione ULID richiede la libreria ulid (disponibile per JavaScript, Python, Go, Rust e altri):

JavaScript (ulid npm)
// Using the 'ulid' npm package
import { ulid } from 'ulid'

const id = ulid()          // "01ARZ3NDEKTSV4RRFFQ69G5FAV"
const id2 = ulid()         // "01ARZ3NDEKXXXXXXXXXXXX..." (same ms, incremented random)

// With a custom timestamp
const id3 = ulid(1469918176385) // deterministic time portion

// Extract the timestamp back out
import { decodeTime } from 'ulid'
decodeTime(id)  // → 1469918176385 (Unix ms)
Python (python-ulid)
# Using python-ulid
from ulid import ULID

uid = ULID()
str(uid)                    # "01ARZ3NDEKTSV4RRFFQ69G5FAV"
uid.timestamp               # 1469918176.385
uid.datetime                # datetime(2016, 7, 30, 23, 36, 16, 385000, tzinfo=timezone.utc)

# Parse an existing ULID string
parsed = ULID.from_str("01ARZ3NDEKTSV4RRFFQ69G5FAV")
parsed.timestamp            # 1469918176.385
PostgreSQL
-- Store ULIDs as TEXT or CHAR(26)
CREATE TABLE events (
  id   CHAR(26) PRIMARY KEY DEFAULT gen_ulid(),  -- if using a PG extension
  name TEXT NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Or pass the ULID from your application layer
INSERT INTO events (id, name, created_at)
VALUES ('01ARZ3NDEKTSV4RRFFQ69G5FAV', 'user.signup', NOW());

-- Range queries are efficient because ULIDs sort chronologically
SELECT * FROM events
WHERE id > '01ARZ3NDEKTSV4RRFFQ69G5FAV'
ORDER BY id
LIMIT 20;
JavaScript (pure — no dependencies)
// Pure browser / Deno / Node.js implementation (no dependencies)
const CROCKFORD = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'

function encodeTime(ms) {
  let result = '', n = BigInt(ms)
  for (let i = 9; i >= 0; i--) {
    result = CROCKFORD[Number(n & 31n)] + result
    n >>= 5n
  }
  return result
}

function encodeRandom(bytes) {
  let n = 0n
  for (const b of bytes) n = (n << 8n) | BigInt(b)
  let result = ''
  for (let i = 15; i >= 0; i--) {
    result = CROCKFORD[Number(n & 31n)] + result
    n >>= 5n
  }
  return result
}

function generateULID() {
  const randomBytes = new Uint8Array(10)
  crypto.getRandomValues(randomBytes)
  return encodeTime(Date.now()) + encodeRandom(randomBytes)
}

Domande Frequenti

Gli ULID sono globalmente univoci?
Sì — con altissima probabilità. Il componente casuale a 80 bit fornisce 2^80 ≈ 1,2 × 10^24 valori possibili per millisecondo per generatore. La probabilità di una collisione all'interno dello stesso millisecondo tra diversi generatori è trascurabile per qualsiasi sistema pratico. Per garanzie di unicità strette, usare la modalità monotona, che incrementa il componente casuale per gli ID generati nello stesso millisecondo.
Un ULID è uguale a un UUID?
No — ULID e UUID sono codifiche diverse di un valore a 128 bit. Un ULID non può essere direttamente sostituito a un UUID in sistemi che validano il formato UUID (esadecimale con trattini). Tuttavia, è possibile convertire tra rappresentazioni binarie ULID e UUID se necessario.
Come funziona la modalità monotona ULID?
In modalità standard, il componente casuale a 80 bit è generato fresh per ogni ULID. In modalità monotona, quando più ULID sono generati nello stesso millisecondo, il componente casuale del secondo e degli ID successivi è il valore casuale precedente più uno. Questo garantisce un ordinamento strettamente monotono all'interno di un millisecondo su un singolo generatore.
Posso decodificare un ULID per ottenere il timestamp di generazione?
Sì. I primi 10 caratteri Crockford Base32 codificano il timestamp Unix a 48 bit in millisecondi. Decodificare quei caratteri usando l'alfabeto Crockford Base32, interpretare il risultato come timestamp Unix in millisecondi e convertire in data. Questo strumento fa esattamente questo.
ULID è uno standard ufficiale?
No. ULID è una specifica comunitaria mantenuta su ulid.github.io. Non è uno standard IETF come UUID. Ciò significa che non c'è un RFC ufficiale a cui fare riferimento e le implementazioni possono variare leggermente. UUID v7 (RFC 9562, 2024) è l'alternativa standardizzata dall'IETF con simili proprietà di ordinamento temporale.