ULID-generaattori - Luo Yksilölliset Lajiteltavat ID:t Verkossa Ilmaiseksi
Ilmainen ULID-generaattorityökalu luo yksilöllisiä, lajiteltavia tunnisteita välittömästi. Luo kryptografisesti turvalliset ULID-tunnisteet tietokannoille, API:ille ja hajautetuille järjestelmille verkossa.
ULID-generaattori
Dokumentaatio
Mikä on ULID?
ULID (Universally Unique Lexicographically Sortable Identifier) on 26 merkin pituinen koodi, jota käytetään tietokannan tietueiden tai hajautetun järjestelmän tapahtumien merkitsemiseen. Se yhdistää nykyisen ajan satunnaiseen dataan, minkä ansiosta aakkosjärjestyksessä lajiteltu ULID-lista on ajan suhteen lajiteltu siinä järjestyksessä, jossa kukin tunniste luotiin. ULID-generaattori on työkalu, joka tuottaa näitä tunnisteita.
ULID:n rakenne
ULID koostuu kahdesta osasta, jotka kirjoitetaan peräkkäin ilman erotinmerkkiä:
- Aikaleima (10 merkkiä): millisekuntien määrä 1. tammikuuta 1970 (Unix-epookki) alkaen, kattaen enintään 48 bittiä.
- Satunnaisuus (16 merkkiä): 80 bittiä satunnaista dataa, jotta kahdella samassa millisekunnissa luodulla ULID:llä on silti lähes varmasti eri arvot.
Molemmat osat on kirjoitettu Crockfordin Base32-aakkostolla: numeroita 0–9 ja kirjaimia A–Z, mutta kirjaimet I, L, O ja U on jätetty pois, koska ne voidaan sekoittaa numeroihin 0 ja 1 tai toisiinsa. Tuloksena on merkkijono, joka on lyhyt, helppo lukea ääneen ja turvallinen sijoittaa suoraan URL-osoitteeseen.
ULID:n laskenta
ULID-generaattori muodostaa tunnisteen kolmessa vaiheessa.
- Luetaan nykyinen aika millisekunteina Unix-epookista. Tämä on 48-bittinen luku.
- Generoidaan 80 bittiä satunnaista dataa, tyypillisesti kryptografisesti turvallisesta satunnaislukugeneraattorista, jolloin saadaan 10 satunnaista tavua.
- Koodataan molemmat osat erikseen Crockfordin Base32-aakkostolla: 48-bittinen aikaleima muuttuu 10 merkiksi ja 80-bittinen satunnaisarvo 16 merkiksi. Koodatut merkkijonot yhdistetään lopulliseksi 26 merkin pituiseksi ULID:ksi.
Koodaus toimii lukemalla kerrallaan 5 bittiä, koska jokainen Base32-merkki edustaa yhtä 32:sta mahdollisesta arvosta (2⁵ = 32). Kymmenen merkkiä mahtuu enimmillään 50 bittiin, mikä riittää 48-bittiselle aikaleimalle. Kuusitoista merkkiä mahtuu täsmälleen 80 bittiin, mikä vastaa satunnaisuusosan kokoa ilman ylijäämää.
Laskentaesimerkki
Oletetaan, että ULID-generaattori käynnistetään aikaleiman 1712345678901 (millisekuntia epookista) kohdalla ja generoi satunnaiset tavut [12, 240, 88, 3, 199, 45, 6, 231, 128, 17].
Vaihe 1 – aikaleiman koodaus. Jaetaan luku 1712345678901 toistuvasti 32:lla, otetaan kunkin jaon jakojäännös ja luetaan numerot lopusta alkuun (tämä on tavanomainen kantalukujärjestelmään muunnos). Tuloksena saadaan 10 merkin mittainen merkkijono 01HTQW311N.
Vaihe 2 – satunnaisuuden koodaus. Pakataan 10 satunnaista tavua 80-bittiseen jonoon ja luetaan 5 bittiä kerrallaan, muuttaen jokainen 5-bittinen palanen Base32-merkiksi. Tuloksena saadaan 16 merkin mittainen merkkijono 1KR5G0Y75M3EF00H.
Vaihe 3 – osien yhdistäminen. Lopullinen ULID on:
101HTQW311N 1KR5G0Y75M3EF00H
2ilman väliä: 01HTQW311N1KR5G0Y75M3EF00H, yhteensä 26 merkkiä.
Tämän generaattorin käyttö
Generaattori toimii selaimessa. Jokainen ULID muodostetaan selaimen kellosta ja 80 tuoreesta satunnaisbitistä, jotka otetaan selaimen kryptografisesta satunnaislukugeneraattorista, joten tunnistetta ei lähetetä palvelimelle eikä sieltä haeta.
- ULID:ien lukumäärä hyväksyy kokonaisluvun väliltä 1–50. Oletusarvo on 1.
- Generoi ULID tuottaa uuden erän. Lukumäärän muuttaminen tuottaa myös uuden erän.
- Kopioi-toiminto kopioi kyseisen yksittäisen ULID:n. Jos näytetään useampia, Kopioi kaikki kopioi koko listan, yksi ULID riviä kohti.
- Tyhjennä palauttaa lukumäärän takaisin yhteen ja generoi yhden uuden ULID:n.
Tulosten alla oleva ULID-rakenne -paneeli jakaa erän ensimmäisen ULID:n kahteen osaan: ensimmäiset 10 merkkiä (aikaleima) ja loput 16 (satunnaisuus).
ULID vs UUID
| Ominaisuus | ULID | UUID (versio 4) |
|---|---|---|
| Pituus | 26 merkkiä | 36 merkkiä (väliviivoilla) |
| Lajiteltavissa luontiajan mukaan | Kyllä | Ei |
| Koodaus | Crockfordin Base32 | Heksadesimaali |
| Sisältää aikaleiman | Kyllä (48 bittiä) | Ei |
| URL-turvallinen ilman koodausta | Kyllä | Kyllä |
Molemmat formaatit pyrkivät tuottamaan tunnisteita, jotka ovat yksilöllisiä järjestelmissä ilman keskusviranomaista numeroiden jakajaa. Tärkein käytännön ero on lajittelu: koska ULID alkaa aikaleimalla, ULID:t lajitellaan pelkkänä tekstinä myös aikajärjestyksessä. Satunnaisella UUID:llä ei ole tätä ominaisuutta, sillä jokainen sen osa on satunnaista.
Yleiset käyttötapaukset
- Tietokantojen primaariavaimet: ULID:t mahdollistavat uusien rivien indeksoinnin suunnilleen lisäysjärjestyksessä, mikä monissa tietokannan moottoreissa käsitellään tehokkaammin kuin täysin satunnaiset avaimet.
- Hajautetut järjestelmät: eri palvelimet voivat luoda ULID:t itsenäisesti ilman koordinointia keskuslaskurin kanssa, ja tulokset ovat silti lähes varmasti yksilöllisiä.
- Tapahtumalokit: koska ULID:t lajittelevat ajan mukaan, tapahtumatunnisteiden lista voi toimia yhtä aikaa myös karkeana aikajanana.
- API- ja tiedostotunnisteet: vakiona 26 merkin mittainen, URL-turvallinen muoto toimii siististi verkkosoitteissa ja tiedostonimissä.
Vastaaviin tunnisteformaatteihin kuuluvat KSUID, joka myös koodaa aikaleiman lajittelua varten, sekä Snowflake ID:t, joita Twitter ja muut käyttävät ja jotka yhdistävät aikaleiman, koneen tunnisteen ja laskurin.
Koodiesimerkkejä
Jokainen alla oleva esimerkki muodostaa ULID:n samalla tavalla kuin ULID-spesifikaatio kuvaa: koodataan 48-bittinen aikaleima 10 Crockfordin Base32-merkin mittaiseksi, koodataan 80 bittiä satunnaisuutta 16 lisämerkin verran ja yhdistetään ne.
JavaScript
1const ENCODING_CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
2
3function encodeULID(time, randomBytes) {
4 // Aikaleima: 48 bittiä -> 10 merkkiä
5 let timestampStr = '';
6 let t = time;
7 for (let i = 0; i < 10; i++) {
8 timestampStr = ENCODING_CHARS[t % 32] + timestampStr;
9 t = Math.floor(t / 32);
10 }
11
12 // Satunnaisuus: 80 bittiä -> 16 merkkiä
13 let result = timestampStr;
14 let bits = 0;
15 let bitCount = 0;
16 for (let i = 0; i < 10; i++) {
17 bits = (bits << 8) | randomBytes[i];
18 bitCount += 8;
19 while (bitCount >= 5) {
20 bitCount -= 5;
21 result += ENCODING_CHARS[(bits >> bitCount) & 0x1f];
22 }
23 }
24 return result;
25}
26
27function generateULID() {
28 const randomBytes = crypto.getRandomValues(new Uint8Array(10));
29 return encodeULID(Date.now(), randomBytes);
30}
31
32console.log(generateULID());
33Python
1import os
2import time
3
4ENCODING_CHARS = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
5
6def encode_ulid(time_ms, random_bytes):
7 # Aikaleima: 48 bittiä -> 10 merkkiä
8 chars = []
9 t = time_ms
10 for _ in range(10):
11 chars.append(ENCODING_CHARS[t % 32])
12 t //= 32
13 timestamp_part = "".join(reversed(chars))
14
15 # Satunnaisuus: 80 bittiä -> 16 merkkiä
16 bits = 0
17 bit_count = 0
18 random_part = []
19 for byte in random_bytes:
20 bits = (bits << 8) | byte
21 bit_count += 8
22 while bit_count >= 5:
23 bit_count -= 5
24 random_part.append(ENCODING_CHARS[(bits >> bit_count) & 0x1F])
25
26 return timestamp_part + "".join(random_part)
27
28def generate_ulid():
29 return encode_ulid(int(time.time() * 1000), os.urandom(10))
30
31print(generate_ulid())
32Java
1import java.security.SecureRandom;
2import java.time.Instant;
3
4public class ULIDGenerator {
5 private static final SecureRandom random = new SecureRandom();
6 private static final char[] ENCODING_CHARS =
7 "0123456789ABCDEFGHJKMNPQRSTVWXYZ".toCharArray();
8
9 public static String generateULID() {
10 long timestamp = Instant.now().toEpochMilli();
11 byte[] randomBytes = new byte[10];
12 random.nextBytes(randomBytes);
13
14 // Aikaleima: 48 bittiä -> 10 merkkiä
15 char[] timestampChars = new char[10];
16 long t = timestamp;
17 for (int i = 9; i >= 0; i--) {
18 timestampChars[i] = ENCODING_CHARS[(int) (t % 32)];
19 t /= 32;
20 }
21
22 // Satunnaisuus: 80 bittiä -> 16 merkkiä
23 StringBuilder result = new StringBuilder(new String(timestampChars));
24 long bits = 0;
25 int bitCount = 0;
26 for (byte b : randomBytes) {
27 bits = (bits << 8) | (b & 0xFF);
28 bitCount += 8;
29 while (bitCount >= 5) {
30 bitCount -= 5;
31 result.append(ENCODING_CHARS[(int) ((bits >> bitCount) & 0x1F)]);
32 }
33 }
34 return result.toString();
35 }
36
37 public static void main(String[] args) {
38 System.out.println(generateULID());
39 }
40}
41Usein kysytyt kysymykset
Mihin ULID:tä käytetään? ULID:tä käytetään yksilöllisenä tunnisteena tietokannan riville, API-resurssille tai kirjattuun tapahtumaan tapauksissa, joissa luontiaikaan lajitteleminen on myös hyödyllistä.
Kuinka pitkä ULID on? ULID on aina 26 merkkiä pitkä: 10 merkkiä aikaleimaa ja 16 satunnaisosaa varten.
Mitä koodausta ULID käyttää? ULID käyttää Crockfordin Base32-aakkostoa, joka koostuu numeroista 0–9 ja kirjaimista A–Z, mutta kirjaimet I, L, O ja U on poistettu sekaannusten välttämiseksi.
Miten ULID eroaa UUID:sta? ULID lajittelee luontiajan mukaan ja on 26 merkkiä pitkä. Satunnainen (versio 4) UUID ei lajittele ajan mukaan ja on 36 merkkiä pitkä väliviivat mukaan lukien.
Kuinka todennäköistä on kahden ULID:n törmääminen? 80 bittiä satunnaisuutta tekee törmäyksistä äärimmäisen epätodennäköisiä. Saman millisekunnin aikana olisi generoitava noin 1,3 biljoonaa ULID:tä, jotta törmäyksen todennäköisyys olisi 50 %.
Voiko ULID:iä generoida ilman internet-yhteyttä? Kyllä. ULID tarvitsee vain paikallisen kellon ja satunnaislukugeneraattorin, joten se voidaan luoda täysin offline-tilassa.
Viitteet
- "ULID Specification." GitHub, https://github.com/ulid/spec.
- "Crockford's Base32 Encoding." http://www.crockford.com/base32.html.