Liigu sisu juurde

ULID generaator - Looge tasuta ainulaadsed sorteeritavad ID-d võrgus

Tasuta ULID generaatori tööriist loob koheselt ainulaadsed, sorteeritavad identifikaatorid. Genereeri krüptograafiliselt turvalised ULID-id andmebaasidele, API-dele ja hajutatud süsteemidele võrgus.

ULID generaator

Tulemuse nägemiseks sisesta väärtus
Laadimiskalkulaator...
📚

Dokumentatsioon

Mis on ULID?

ULID (Universally Unique Lexicographically Sortable Identifier ehk universaalselt unikaalne leksikograafiliselt sorditav identifikaator) on 26-märgiline kood, mida kasutatakse andmebaasis kirjete või hajussüsteemis sündmuste tähistamiseks. See ühendab praeguse aja juhuslike andmetega, nii et tähestikuliselt sorditud ULID-ide loend sortitakse ühtlasi iga identifikaatori loomise aja järgi. ULID-i generaator on tööriist, mis neid koode loob.

ULID-i struktuur

ULID-il on kaks järjestikku kirjutatavat osa, mille vahel pole eraldajat:

  • Ajatempel (10 märki): millisekundite arv alates kuupäevast 1. jaanuar 1970 (Unixi ajastu), mahutades kuni 48 bitti.
  • Juhuslikkus (16 märki): 80 bitti juhuslikke andmeid, mis lisatakse selleks, et samal millisekundil loodud kaks ULID-i oleksid peaaegu kindlasti erinevad.

Mõlemad osad kirjutatakse Crockfordi Base32 tähestikku kasutades: numbrid 0–9 ning tähed A–Z, välja arvatud I, L, O ja U. Need neli tähte jäetakse välja, sest neid võib segi ajada numbritega 0 ja 1 või üksteisega. Tulemuseks on lühike ja hõlpsasti ette loetav string, mida võib otse URL-i lisada.

Kuidas ULID-i arvutada

ULID-i generaator moodustab identifikaatori kolme sammuga.

  1. Loe praegune aeg millisekundites alates Unixi ajastust. See on 48-bitine arv.
  2. Genereeri 80 bitti juhuslikke andmeid, tavaliselt krüptograafiliselt turvalise juhuslike arvude generaatori abil, saades 10 juhuslikku baiti.
  3. Kodeeri mõlemad osad eraldi, kasutades Crockfordi Base32 tähestikku: 48-bitine ajatempel muutub 10 märgiks ja 80-bitine juhuslik väärtus 16 märgiks. Kaks kodeeritud stringi ühendatakse lõplikuks 26-märgiliseks ULID-iks.

Kodeerimisel loetakse arvu 5 biti kaupa, sest iga Base32 märk esindab üht 32 võimalikust väärtusest (2^5 = 32). Kümme märki mahutavad kuni 50 bitti, millest piisab 48-bitise ajatempli jaoks. Kuusteist märki mahutavad täpselt 80 bitti, mis vastab juhuslikkuse osale, ilma ülejäägita.

Lahendatud näide

Oletame, et ULID-i generaator töötab ajatempliga 1712345678901 (millisekundid alates ajastust) ja saab juhuslikud baidid [12, 240, 88, 3, 199, 45, 6, 231, 128, 17].

1. samm – ajatempli kodeerimine. Jaga 1712345678901 korduvalt 32-ga, võttes iga kord jäägi ja lugedes saadud numbreid viimasest esimeseni (see on tavaline arvusüsteemi teisendus). Tulemuseks saadakse 10-märgiline string 01HTQW311N.

Samm 2 – juhuslikkuse kodeerimine. Paiguta 10 juhuslikku baiti 80-bitiseks vooks, seejärel loe seda 5 biti kaupa ja teisenda iga 5-bitine rühm Base32 märgiks. Tulemuseks saadakse 16-märgiline string 1KR5G0Y75M3EF00H.

Samm 3 – osade ühendamine. Lõplik ULID on:

101HTQW311N 1KR5G0Y75M3EF00H
2

ilma tühikuta: 01HTQW311N1KR5G0Y75M3EF00H, kokku 26 märki.

Selle generaatori kasutamine

Generaator töötab brauseris. Iga ULID moodustatakse brauseri kella ja brauseri krüptograafilisest juhuslike arvude generaatorist saadud 80 uue juhusliku biti põhjal, seega ei saadeta ühtki identifikaatorit serverisse ega hangita seda serverist.

  • ULID-ide arv lubab sisestada täisarvu vahemikus 1 kuni 50. Vaikimisi on väärtus 1.
  • Genereeri ULID loob uue kogumi. Ka arvu muutmine loob uue kogumi.
  • Tulemuse juures olev Kopeeri kopeerib selle ühe ULID-i. Kui kuvatakse rohkem kui üks ULID, kopeerib Kopeeri kõik kogu loendi, ühe ULID-i reale.
  • Lähtesta määrab arvuks taas 1 ja genereerib ühe uue ULID-i.

Tulemuste all jaotab ULID-i struktuuri paneel kogumi esimese ULID-i kaheks osaks: esimesed 10 märki (ajatempel) ja viimased 16 märki (juhuslikkus).

ULID ja UUID

OmadusULIDUUID (versioon 4)
Pikkus26 märki36 märki (sidekriipsudega)
Loomise aja järgi sorditavJahEi
KodeeringCrockfordi Base32Kuueteistkümnendsüsteem
Sisaldab ajatemplitJah (48 bitti)Ei
URL-i jaoks turvaline ilma kodeerimisetaJahJah

Mõlema vormingu eesmärk on luua süsteemideüleselt unikaalseid identifikaatoreid, ilma et keskne asutus numbreid väljastaks. Peamine praktiline erinevus seisneb sortimises: kuna ULID algab ajatempliga, sorditakse lihttekstina sortides ULID-id ka aja järgi. Juhuslikul UUID-l seda omadust pole, sest kõik selle osad on juhuslikud.

Levinud kasutusalad

  • Andmebaasi primaarvõtmed: ULID-id võimaldavad andmebaasil indekseerida uued read ligikaudu nende lisamise järjekorras, mida paljud andmebaasimootorid käsitlevad tõhusamalt kui täiesti juhuslikke võtmeid.
  • Hajussüsteemid: eraldi serverid saavad ULID-e iseseisvalt genereerida, ilma keskse loenduriga kooskõlastamata, ning tulemused on siiski peaaegu kindlasti unikaalsed.
  • Sündmuste logid: kuna ULID-id sorteeruvad aja järgi, võib sündmuste ID-de loend toimida ligikaudse ajajoonena.
  • API-de ja failide identifikaatorid: fikseeritud 26-märgiline URL-i jaoks turvaline vorming sobib hästi veebiaadressidesse ja failinimedesse.

Seotud identifikaatorivormingute hulka kuuluvad KSUID, mis kodeerib samuti sortimise jaoks ajatempli, ning Snowflake’i ID-d, mida kasutavad Twitter ja teised ning mis ühendavad ajatempli, masina identifikaatori ja loenduri.

Koodinäited

Kõik allolevad näited moodustavad ULID-i samamoodi, nagu ULID-i spetsifikatsioon kirjeldab: kodeerivad 48-bitise ajatempli 10 Crockfordi Base32 märgiks, kodeerivad 80 bitti juhuslikkust veel 16 märgiks ja ühendavad need.

JavaScript

1const ENCODING_CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
2
3function encodeULID(time, randomBytes) {
4  // Timestamp: 48 bits -> 10 characters
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  // Randomness: 80 bits -> 16 characters
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());
33

Python

1import os
2import time
3
4ENCODING_CHARS = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
5
6def encode_ulid(time_ms, random_bytes):
7    # Timestamp: 48 bits -> 10 characters
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    # Randomness: 80 bits -> 16 characters
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())
32

Java

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        // Timestamp: 48 bits -> 10 characters
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        // Randomness: 80 bits -> 16 characters
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}
41

Korduma kippuvad küsimused

Milleks ULID-i kasutatakse? ULID-i kasutatakse andmebaasirea, API ressursi või logitud sündmuse unikaalse identifikaatorina juhul, kui kasulik on ka loomise aja järgi sortimine.

Kui pikk on ULID? ULID on alati 26 märki pikk: ajatemplile kulub 10 märki ja juhuslikule osale 16 märki.

Millist kodeeringut ULID kasutab? ULID kasutab Crockfordi Base32 tähestikku, mis koosneb numbritest 0–9 ja tähtedest A–Z, millest on segiajamise vältimiseks eemaldatud I, L, O ja U.

Mille poolest erineb ULID UUID-st? ULID sorteerub loomise aja järgi ja on 26 märki pikk. Juhuslik UUID (versioon 4) ei sorteeru aja järgi ning on koos sidekriipsudega 36 märki pikk.

Kui tõenäoline on kahe ULID-i kokkupõrge? 80 bitti juhuslikkust muudavad kokkupõrked äärmiselt ebatõenäoliseks. Ligikaudu 1,3 triljoni ULID-i genereerimine sama millisekundi jooksul oleks vajalik, et tõenäosus kahe identifikaatori ühesuguseks osutumiseks jõuaks väärtuseni 50%.

Kas ULID-e saab genereerida ilma internetiühenduseta? Jah. ULID vajab ainult kohalikku kella ja juhuslike arvude generaatorit, seega saab selle luua täielikult võrguühenduseta.

Viited

  1. "ULID Specification." GitHub, https://github.com/ulid/spec.
  2. "Crockford's Base32 Encoding." http://www.crockford.com/base32.html.