UUID-urile și ULID-urile sunt scheme pentru emiterea de ID-uri „global unice” în sisteme distribuite și în baze de date. Par asemănătoare la prima vedere, însă se diferențiază prin statutul de standardizare, eficiența de stocare, lizibilitatea pentru oameni și riscul de expunere a informațiilor. În acest articol compar cele trei abordări dominante — UUID v4, UUID v7 și ULID — și închei cu note despre UUID v1 și UUID v6.

Dacă vrei să experimentezi direct în browser, instrumentele de mai jos se ocupă integral local de generare și de extragerea timestamp-urilor:


UUID v4: varianta pur aleatoare

UUID v4 rezervă 122 din cei 128 de biți pentru aleator și menține probabilitatea de coliziune infimă. Compromisul este că ID-urile nu sunt ordonate cronologic, astfel încât indexurile B-tree suferă divizări frecvente de pagini și localitate slabă la inserare. Folosește v4 pentru ID-uri de sesiune, tokenuri de unică folosință și alte cazuri în care „pură aleatoritate” este suficientă.


UUID v7: evoluția orientată temporal

Standardizat în RFC 9562 (2024), UUID v7 stochează timestamp-ul UNIX în milisecunde în primii 48 de biți, iar restul îi umple cu aleator. Ordinea lexicografică (comparare de șiruri) devine identică cu ordinea de generare (cronologică), astfel încât poți folosi v7 ca cheie primară păstrând localitatea indexului. Încape nativ în coloane existente uuid sau BINARY(16).

  • Buget de aleator: în afară de biții de versiune și variantă, v7 păstrează aproximativ 74 de biți aleatori. Unele implementări aplică ajustări monotone pentru a evita coliziunile în aceeași milisecundă; chiar și așa, entropia rămâne mai mult decât suficientă.
  • Expunere de informații: ID-ul dezvăluie momentul generării cu precizie de milisecundă. Combinația dintre un generator slab de aleator și un volum mare de emitere poate face mai ușoară ghicirea ID-urilor vecine. În sisteme interne este, de regulă, acceptabil, însă endpoint-urile publice care trebuie să ascundă ordinea ar trebui să evalueze riscul.

ULID: identificatori prietenoși cu oamenii

Propus în 2016, ULID a devenit un standard de facto. Folosește Crockford Base32 (26 de caractere), eliminând caracterele care se confundă ușor (O/I/L/1), ceea ce îl face potrivit pentru URL-uri și fluxuri de copiere. Ca și v7, primii 48 de biți stochează timestamp-ul (ms), deci ordinea lexicografică coincide cu ordinea cronologică.

  • Garanția de sortare: șirurile ULID se compară întotdeauna în ordine cronologică, iar generarea monotonică este larg implementată pentru a evita coliziunile.
  • Format de stocare: păstrează lizibilitatea dacă îl salvezi ca TEXT(26), însă pentru stocare binară ai nevoie de conversie Base32↔16 octeți. Majoritatea tipurilor UUID din RDBMS nu acceptă ULID direct.
  • Expunere de informații: la fel ca v7, ULID dezvăluie timestamp-uri (ms). Dacă nu vrei ca utilizatorii API-ului să deducă ordinea emiterii, caută alternative.

Tabel comparativ (perspectivă practică)

Aspect UUID v4 UUID v7 ULID
Standardizare RFC 4122 RFC 9562 (2024) Standard de facto
Structură pe biți 128 de biți din care 122 aleatori 48 biți = UNIX ms + rest aleatori (monotonic opțional) 48 biți = UNIX ms + 80 biți aleatori
Entropie aleatoare ≈122 biți ≈74 biți (după versiune/variantă) 80 biți
Formă textuală 36 de caractere (hex + cratime) 36 de caractere (hex + cratime) 26 de caractere (Crockford Base32)
Ordine naturală (text) ✗ (aleator) ✓ (lexicografică = cronologică) ✓ (lexicografică = cronologică)
Ordine naturală (binar) ✓ (BINARY(16) ordonează crescător în timp) ✓ (dacă cei 6 biți de timp sunt în față, big-endian)
Potrivire în baza de date uuid / BINARY(16) sunt ideale uuid / BINARY(16) sunt ideale TEXT(26) pentru lizibilitate; BINARY(16) cere conversie și multe tipuri UUID îl resping
Localitatea indexului Slabă (inserări aleatoare) Bună (inserări la coadă, cu atenție la hot spot-uri) Bună (la fel ca mai sus)
Costul verificării egalității Redus (comparare 16 octeți) Redus (comparare 16 octeți) TEXT costă mai mult; BINARY e redus
Cost de codare Hex↔16 octeți (ieftin) Hex↔16 octeți (ieftin) Base32↔16 octeți (mai greu)
Lizibilitate pentru oameni / potrivire pentru URL Redusă Redusă Ridicată
Expunere de informații Niciuna Timestamp (ms) Timestamp (ms)
Utilizări tipice ID-uri de sesiune, tokenuri de unică folosință Chei primare în DB, ID-uri de eveniment, ordonare în loguri ID-uri publice și loguri unde contează lizibilitatea

Atenție la hot spot-uri pentru v7/ULID: dacă toate inserările merg către un singur shard sau lider, fluxurile heavy append pot produce totuși pagini B-tree supraîncălzite. Tehnici precum „prefix shuffle” sau combinarea ID-ului cu o cheie de shard ajută la distribuirea încărcării.


Note de implementare în baze de date (PostgreSQL / MySQL / SQLite)

  • PostgreSQL
    • v4/v7: tipul uuid este optim. Importă v7 ca text, convertește la uuid și beneficiezi de ordonarea naturală.
    • ULID: folosește char(26) / text sau bytea(16) după conversie Base32.
  • MySQL / InnoDB
    • BINARY(16) este compact și rapid pentru v4/v7. v7 rămâne ordonat temporal în formă big-endian. ULID merge în CHAR(26) sau BINARY(16) cu conversie.
  • SQLite
    • Nu are tip UUID dedicat. Folosește BLOB(16) sau TEXT. BLOB-urile indexate sunt relativ eficiente.

Din perspectiva securității

  • Rezistență la ghicire: v7/ULID împart biții de timp, astfel încât, dacă emiți masiv în aceeași milisecundă și generatorul de aleator este slab, crește riscul de ghicire a ID-urilor vecine. Folosește un PRNG criptografic și minimizează biasul din generarea monotonică.
  • Scurgere de metadate: din ID se pot deduce momentul generării și volumul aproximativ emis. Pentru API-urile publice este mai sigur să separi ID-urile interne de cele expuse extern.

Concluzie (ghid de selecție)

  • UUID v4: complet aleator. Nu oferă sortare naturală. Există un risc teoretic infim de coliziune. Potrivit pentru ID-uri de sesiune sau tokenuri CSRF.
  • UUID v7: UUID ordonat, standardizat. Ideal pentru sisteme interne și chei primare în baze de date. Generarea monotonică face ca riscul practic de coliziune să fie neglijabil.
  • ULID: (mai) prietenos cu oamenii. Este util când lizibilitatea în URL-uri sau loguri este importantă, dar ca cheie internă de bază de date pierde adesea în fața v7. Și aici generarea monotonică ține coliziunile departe în practică.

Supliment: UUID v1 / v6 (pe scurt)

  • UUID v1 (bazat pe timp + MAC) 60 de biți de timestamp (la 100 ns) + un identificator de nod (de obicei adresa MAC). Oferă ordonare temporală bună, dar dezvăluie informații despre gazdă și necesită grijă la regresia ceasului. În prezent este descurajat din motive de confidențialitate.

  • UUID v6 (versiune reordonată a v1) Rearanjează timestamp-ul lui v1 în big-endian pentru a îmbunătăți sortarea. Istoric a fost privit ca „v1 care se sortează mai ușor”, însă standardizarea a convergent către v7. În proiecte noi este recomandat v7 în locul v6.


Anexă: listă de verificare pentru implementare

  • Folosește PRNG criptografic furnizat de sistemul de operare.
  • Generarea monotonică pentru v7/ULID evită coliziunile în aceeași milisecundă și menține aleatorul uniform.
  • În baze de date, preferă tipurile uuid / BINARY(16) și evită degradarea de performanță cauzată de collation pe șiruri.
  • Definește politica de ID-uri publice (expunerea ordinii). Dacă este nevoie, separă ID-urile interne de cele externe.

Concluzia tehnică de astăzi: pentru cheile primare interne, UUID v7 este alegerea principală, iar pentru scenarii în care lizibilitatea contează sau apar constrângeri speciale, ULID rămâne o alternativă serioasă. Doar cerințe neobișnuite (de exemplu interdicția totală de a expune timestamp-uri) justifică revenirea la v4 sau la alte scheme.