Generatore ULID
Genera identificatori unici ordinabili lessicograficamente
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:
| Timestamp | Casuale |
|---|---|
| 01ARZ3NDEK | TSVE4RRFFQ69G5FAV |
| 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
Quattro caratteri sono intenzionalmente esclusi:
IeLsono esclusi — facilmente confusi con la cifra1Oè escluso — facilmente confuso con la cifra0Uè escluso — evita parole oscene accidentali negli ID generati- La codifica è
case-insensitive—01ARZ3NDEKTSV4RRFFQ69G5FAVe01arz3ndektsv4rrffq69g5favsono 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à | ULID | UUID |
|---|---|---|
| Formato | Crockford Base32 | Esadecimale con trattini |
| Lunghezza | 26 caratteri | 36 caratteri |
| Timestamp | Unix ms a 48 bit | Nessuno (v4) o ms a 48 bit (v7) |
| Ordinabile | Sì — lessicografico | No (v4) / Sì (v7) |
| URL-safe | Sì (solo alfanumerico) | Sì (con trattini) |
| Dipendenze | Richiede libreria | Nativo (crypto.randomUUID) |
| Supporto DB | Memorizzare come CHAR(26) o BINARY(16) | Tipo colonna UUID nativo |
| Spec | Spec 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à | ULID | NanoID |
|---|---|---|
| Timestamp | Sì — Unix ms a 48 bit | No |
| Ordinabile | Sì | No |
| Lunghezza predefinita | 26 caratteri | 21 caratteri |
| Entropia | 80 bit (componente casuale) | ~126 bit |
| Alfabeto | Crockford Base32 (32 chars) | URL-safe Base64 (64 chars) |
| Lunghezza personalizzabile | No | Sì (qualsiasi lunghezza) |
| Caso d'uso | ID ordinati nel tempo, chiavi primarie DB | Token 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:
Esempi di codice
La generazione ULID richiede la libreria ulid (disponibile per JavaScript, Python, Go, Rust e altri):
// 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)# 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-- 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;// 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)
}