Unutrašnja struktura ULID‑a i kako ga ovaj generator kreira
Svaki ULID je 26‑karakterni niz koji se u potpunosti sastoji od Crockfordovog base32 alfabeta. Prvih 10 karaktera kodiraju vremensku oznaku s preciznošću milisekunde (48 bita), a preostalih 16 karaktera nose slučajnu komponentu (80 bita). Ukupno 128 bita, koliko ima i UUID v4, ali u znatno kraćem prikazu: 26 umjesto 36 karaktera.
Generator na ovoj stranici uzima format ULID iz padajućeg menija, a korisnik bira broj identifikatora (od 1 do 100). Svaki poziv generisanja proizvodi novu vremensku oznaku zasnovanu na Date.now() u pregledaču i nasumični dio iz crypto.getRandomValues(). Zato se čak i istovremeni klikovi na dugme često mapiraju u različite milisekunde, a kada se dese u istoj milisekundi, redoslijed u listi ne garantuje striktno uređenje po vremenu – moguće je da identifikator koji se fizički pojavi kasnije ima manju vremensku oznaku ako je generisan u istom redu (ali to je rijetko i praktično zanemarljivo).
Ključno je shvatiti da ULID nije UUID; on nema verziju ni varijantu. Njegova jedina svrha jeste da bude sortabilan po vremenu nastanka i istovremeno kompaktan, što ga čini idealnim za primjene gdje UUID v4 izgleda preglomazno.
Crockford base32 – zašto su ULID‑ovi neosjetljivi na velika/mala slova
Umjesto uobičajenog heksadecimalnog alfabeta (0‑9, A‑F) ili base64, ULID koristi Crockfordov base32. Taj set ima 32 znaka: cifre 0‑9 i slova A‑Z, uz izuzetak I, L, O, U. Razlog je uklanjanje vizuelno dvosmislenih znakova – slovo “I” i “L” se lako zamijene, “O” s nulom, “U” s “V” u određenim fontovima. Zato su ULID‑ovi neosjetljivi na velika i mala slova: prilikom dekodiranja “a” i “A” se tretiraju identično, a istovremeno nema crtica koje bi dodatno zakomplikovale kopiranje.
Značajna posljedica: na ovom generatoru ne postoji opcija za uključivanje velikih slova ili crtica, kao što je slučaj kod UUID generatora na istom sajtu. ULID je sam po sebi bez crtica i podrazumijevano se ispisuje malim ili velikim slovima (ovdje se prikazuje onako kako ga generiše Javascript, obično malim slovima, ali korisnik može slobodno pretvoriti u velika – nema gubitka).
| Svojstvo | Crockford base32 (ULID) | Heksadecimalni (UUID) |
|---|---|---|
| Dužina niza | 26 karaktera | 36 karaktera (uključujući 4 crtice) |
| Broj znakova u alfabetu | 32 | 16 |
| Osjetljivost na velika/mala slova | Ne (case‑insensitive) | Da (uobičajeno mala slova) |
| Vizuelno dvosmislene cifre/slova | I, L, O, U su izbačeni | Sve prisutno |
| URL‑sigurnost | Potpuna, bez escape‑ovanja | Zahtijeva crtice koje su bezbedne, ali duže |
Poređenje s UUID v4 i UUID v7
UUID v4 (verzija 4) je potpuno nasumičan i zauzima 128 bita, ali se obično prikazuje kao 36‑karakterni niz sa crticama. To je 10 karaktera duže od ULID‑a i nije sortabilan po vremenu. UUID v7 (verzija 7) uvodi vremensku oznaku sličnu ULID‑u, ali zadržava crtice i još uvijek koristi heksadecimalnu reprezentaciju (36 karaktera). ULID je, dakle, kraći i čistiji – nema crtica, a istovremeno nudi sortabilnost.
Za baze podataka koje koriste B‑stablo (većina relacionih baza), unos nasumično raspoređenih UUID v4 identifikatora može dovesti do fragmentacije indeksa i čestog preuređenja stranica. ULID, naprotiv, proizvodi približno rastući niz novih identifikatora, što poboljšava performanse upisivanja. Ovo je posebno važno za primarne ključeve u distribuiranim sistemima ili za evidencije događaja (event logs) gdje je sortiranje po vremenu uobičajeno.
Sortiranje po vremenu – kako radi i koje su granice
ULID‑ovi su leksikografski sortabilni po vremenu nastanka, jer prvih 10 karaktera (48 bita) predstavljaju milisekundni vremenski pečat, a base32 omogućava da se raniji vremenski pečat pojavi kao manji string. Dakle, ako generišemo dva ULID‑a u razmaku od jedne milisekunde, drugi će leksikografski biti veći od prvog.
Međutim, u okviru iste milisekunde, redoslijed se ne može garantovati jer je slučajni dio potpuno nezavisan od vremena. Generator na ovoj stranici to ne pokušava da riješi – ne dodaje nikakve sekvence ili brojeve redova. Ako više identifikatora bude istovremeno generisano, njihov redoslijed u izlaznoj listi zavisi od redoslijeda unosa u JavaScript petlji, ali to nije isto što i vremensko uređenje. Za većinu aplikacija to je sasvim dovoljno: ULID jamči da će, ako je ID1 generisan prije ID2 (čak i u istoj milisekundi), ID1 biti manji od ID2 samo ako je vremenska oznaka striktno manja – a to gotovo uvijek jeste, osim ako uređaj ima vrlo visoku rezoluciju.
Vjerovatnoća kolizije i sigurnost slučajnog dijela
Slučajni dio ULID‑a ima 80 bita, što znači da postoji 2^80 mogućih vrijednosti. Iako je to ogroman prostor, pri visokim stopama generisanja (hiljade identifikatora u istoj milisekundi) moguće je doći do kolizije ako slučajni generator nije dovoljno kvalitetan. Međutim, crypto.getRandomValues() u modernim pregledačima koristi kriptografski sigurni generator, a tipična primjena (do 100 ID‑ova odjednom na ovoj stranici) čini koliziju praktično nemogućom.
U poređenju s UUID v4 (122 bita slučajnosti), ULID ima nešto manji prostor (80 bita), ali je i dalje dovoljan za većinu aplikacija. Bitno je napomenuti da ULID ne nudi nikakvu garanciju jedinstvenosti izvan jednog generatora – ako više čvorova u distribuiranoj mreži istovremeno generišu ULID‑ove, moguće su kolizije u slučajnom dijelu. Zato se u takvim sistemima često dodaje identifikator čvora (npr. korišćenjem prvih nekoliko karaktera slučajnog dijela). Ovaj generator ne uključuje takav mehanizam, jer je namijenjen za jednostavnu upotrebu na jednom mjestu.
URL‑sigurnost i primjena u bazama podataka
Svi znakovi u ULID‑u (Crockford base32) su rezervisani u RFC 3986, odnosno ne zahtijevaju procentno kodiranje (percent‑encoding) u URL‑ovima. Crtica, tačka, tilde i podvlaka su dozvoljene, ali ULID ne sadrži ni jedan od njih. To znači da se ULID može direktno koristiti u dijelovima URL‑a, query parametrima ili fragmentima bez ikakve transformacije.
Za B‑stablo indeksa, prednost ULID‑a je u tome što novi redovi uvijek dodaju vrijednosti na desnu ivicu stabla (približno), čime se smanjuje potreba za reorganizacijom i čestim dijeljenjem stranica. U praksi, to može značiti 10‑20% bolje performanse upisa u odnosu na potpuno nasumične UUID‑ove. Prirodno, ovo vrijedi samo ako se ULID koristi kao primarni ključ (clustered index). Ako se koristi samo kao jedinstveni identifikator bez indeksa, nema razlike.
Lokalno generisanje i privatnost
Sva generisanja na ovoj stranici odvijaju se isključivo u pregledaču korisnika. Nema slanja podataka na server, nema kolokacije identifikatora, nema praćenja. To je izričito navedeno u pravilima: “All ID generation happens locally in your browser using strong browser randomness; nothing is sent to any server.” Ovo je važno za aplikacije koje zahtijevaju privatnost ili rade u offline okruženju – ULID generator može raditi čak i bez internet konekcije nakon učitavanja stranice.
Generator koristi samo <input type="number"> za broj identifikatora, s opsegom 1 do 100. Vrijednosti izvan tog opsega se ne prihvataju (ili se automatski podešavaju pomoću HTML5 validacije). Svaka promjena formata, broja ili bilo koje druge opcije (ovdje ih nema) ponovo pokreće generisanje i prikazuje status “Ready.”, “Generated.” ili “Copied all!” nakon kopiranja cijele liste.
FAQ – često postavljana pitanja
1. Mogu li koristiti ULID umjesto UUID v4 u svom projektu?
Da, ULID je potpuna zamjena za 128‑bitne identifikatore. Međutim, obratite pažnju na sortabilnost i činjenicu da ULID otkriva približno vrijeme kreiranja (prvih 10 karaktera su vremenska oznaka). Ako vam je potrebna potpuna slučajnost i ne želite da se vidi redoslijed, radije koristite UUID v4.
2. Da li je ULID slučajan ili je deterministički?
Dio od 80 bita je nasumičan (koristeći crypto.getRandomValues()), tako da je svaki ID jedinstven sa velikom vjerovatnoćom. Vremenski dio je deterministički – zavisi od sistemskog sata.
3. Šta se dešava ako generišem 100 ULID‑ova u istoj milisekundi?
Svi će imati istu vremensku oznaku (prvih 10 karaktera), a razlikovaće se samo u slučajnom dijelu. Leksikografski redoslijed tada ne odražava vremenski redoslijed unutar te milisekunde. Ako vam je važan redoslijed unutar iste milisekunde, potrebna vam je dodatna sekvenca (npr. monotoni broj).
4. Zašto nema opcija za velika/mala slova ili crtice?
ULID standard (specifikacija) ne predviđa crtice, a Crockford base32 je inherentno neosjetljiv na veličinu slova. Dodavanje takvih opcija bi bilo zbunjujuće i suprotno svrsi.
5. Koliko vremenski pečat može da traje?
48 bita omogućava predstavljanje milisekundi u rasponu od oko 8.9 miliona godina – odnosno do kraja 10889. godine po julijanskom kalendaru. Sasvim dovoljno.
6. Mogu li generisati ULID na serveru umjesto u pregledaču?
Da, ULID algoritam je jednostavan i postoje implementacije u gotovo svim programskim jezicima. Ovaj generator je samo praktičan alat za brze potrebe u razvoju ili testiranju.