Přeskočit na obsah

Generátor ULID - Vytvořte jedinečná, seřaditelná ID online zdarma

Bezplatný nástroj pro generování ULID vytváří jedinečné, seřaditelné identifikátory okamžitě. Generujte kryptograficky zabezpečené ULID pro databáze, API a distribuované systémy online.

Generátor ULID

Zadejte hodnotu pro zobrazení výsledku
Kalkulačka načítání...
📚

Dokumentace

Co je ULID?

ULID (univerzálně jedinečný lexikograficky řaditelný identifikátor) je kód o délce 26 znaků používaný k označení záznamů v databázi nebo události v distribuovaném systému. Kombinuje aktuální čas s náhodnými daty, takže seznam ULIDů seřazený abecedně skončí seřazený podle času vytvoření jednotlivých identifikátorů. Generátor ULIDů je nástroj, který tyto kódy vytváří.

Struktura ULID

ULID má dvě části zapsané bezprostředně za sebou, bez oddělovače:

  • Časové razítko (10 znaků): počet milisekund od 1. ledna 1970 (unixové epochy), uložený v nejvýše 48 bitech.
  • Náhodná část (16 znaků): 80 bitů náhodných dat přidaných proto, aby se dva ULIDy vytvořené ve stejné milisekundě s téměř jistotou lišily.

Obě části se zapisují pomocí abecedy Crockford Base32: číslic 0–9 a písmen A–Z bez písmen I, L, O a U. Tato čtyři písmena jsou vynechána, protože je lze zaměnit za číslice 0 a 1 nebo navzájem. Výsledkem je krátký řetězec, který se snadno čte nahlas a lze jej bezpečně vložit přímo do adresy URL.

Jak vypočítat ULID

Generátor ULIDu vytvoří identifikátor ve třech krocích.

  1. Načíst aktuální čas v milisekundách od unixové epochy. Jde o číslo o velikosti 48 bitů.
  2. Vygenerovat 80 bitů náhodných dat, obvykle pomocí kryptograficky bezpečného generátoru náhodných čísel, což poskytne 10 náhodných bajtů.
  3. Samostatně zakódovat obě části pomocí abecedy Crockford Base32: časové razítko o velikosti 48 bitů se převede na 10 znaků a náhodná hodnota o velikosti 80 bitů na 16 znaků. Oba zakódované řetězce se spojí do výsledného ULIDu o délce 26 znaků.

Kódování funguje tak, že se číslo čte po 5 bitech, protože každý znak Base32 představuje jednu z 32 možných hodnot (2^5 = 32). Deset znaků pojme až 50 bitů, což stačí pro časové razítko o velikosti 48 bitů. Šestnáct znaků pojme přesně 80 bitů, což odpovídá náhodné části bez zbývajících bitů.

Příklad výpočtu

Předpokládejme, že generátor ULIDu běží v čase 1712345678901 (milisekund od epochy) a vygeneruje náhodné bajty [12, 240, 88, 3, 199, 45, 6, 231, 128, 17].

Krok 1 – zakódování časového razítka. Číslo 1712345678901 opakovaně dělíme číslem 32, pokaždé vezmeme zbytek a číslice čteme od poslední po první (jde o standardní převod číselné soustavy). Vznikne řetězec o délce 10 znaků 01HTQW311N.

Krok 2 – zakódování náhodné části. 10 náhodných bajtů zabalíme do proudu 80 bitů, poté je čteme po 5 bitech a každý blok 5 bitů převedeme na znak Base32. Vznikne řetězec o délce 16 znaků 1KR5G0Y75M3EF00H.

Krok 3 – spojení částí. Výsledný ULID je:

101HTQW311N 1KR5G0Y75M3EF00H
2

zapsaný bez mezery: 01HTQW311N1KR5G0Y75M3EF00H, celkem 26 znaků.

Použití tohoto generátoru

Generátor běží v prohlížeči. Každý ULID se vytvoří z hodin prohlížeče a 80 nových náhodných bitů získaných z kryptografického generátoru náhodných čísel v prohlížeči, takže žádný identifikátor se neposílá na server ani se z něj nenačítá.

  • Počet ULIDů přijímá celé číslo od 1 do 50. Výchozí hodnota je 1.
  • Generovat ULID vytvoří novou dávku. Změnou počtu se rovněž vytvoří nová dávka.
  • Kopírovat u výsledku zkopíruje daný ULID. Pokud je zobrazeno více ULIDů, Kopírovat vše zkopíruje celý seznam, jeden ULID na řádek.
  • Resetovat nastaví počet zpět na 1 a vygeneruje jeden nový ULID.

Pod výsledky panel Struktura ULIDu rozdělí první ULID v dávce na dvě části: prvních 10 znaků (časové razítko) a posledních 16 znaků (náhodná část).

ULID versus UUID

VlastnostULIDUUID (verze 4)
Délka26 znaků36 znaků (s pomlčkami)
Řaditelné podle času vytvořeníAnoNe
KódováníCrockford Base32Šestnáctková
Obsahuje časové razítkoAno (48 bitů)Ne
Bezpečný pro adresy URL bez escapováníAnoAno

Oba formáty mají vytvářet identifikátory jedinečné napříč systémy bez centrální autority, která by přidělovala čísla. Hlavní praktický rozdíl spočívá v řazení: protože ULID začíná časovým razítkem, řazení ULIDů jako prostého textu je zároveň seřadí podle času. Náhodný UUID tuto vlastnost nemá, protože každá jeho část je náhodná.

Běžná použití

  • Primární klíče databází: ULIDy umožňují databázi indexovat nové řádky přibližně v pořadí, v jakém byly vloženy, což mnoho databázových strojů zpracovává efektivněji než zcela náhodné klíče.
  • Distribuované systémy: jednotlivé servery mohou generovat ULIDy nezávisle na sobě, bez koordinace s centrálním počítadlem, a výsledky jsou přesto s téměř jistotou jedinečné.
  • Protokoly událostí: protože se ULIDy řadí podle času, může seznam identifikátorů událostí sloužit také jako přibližná časová osa.
  • Identifikátory API a souborů: pevný formát o délce 26 znaků, bezpečný pro adresy URL, funguje bez problémů ve webových adresách i názvech souborů.

Mezi související formáty identifikátorů patří KSUID, který rovněž kóduje časové razítko pro účely řazení, a identifikátory Snowflake používané Twitterem a dalšími službami, které kombinují časové razítko, identifikátor počítače a počítadlo.

Příklady kódu

Každý níže uvedený příklad vytváří ULID stejným způsobem, jaký popisuje specifikace ULIDu: časové razítko o velikosti 48 bitů zakóduje jako 10 znaků Crockford Base32, 80 bitů náhodných dat zakóduje jako dalších 16 znaků a obě části spojí.

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

Často kladené otázky

K čemu se ULID používá? ULID se používá jako jedinečný identifikátor řádku v databázi, zdroje API nebo zaznamenané události v případech, kdy je užitečné také řazení podle času vytvoření.

Jak dlouhý je ULID? ULID má vždy délku 26 znaků: 10 znaků pro časové razítko a 16 pro náhodnou část.

Jaké kódování ULID používá? ULID používá abecedu Crockford Base32 tvořenou číslicemi 0–9 a písmeny A–Z, z nichž jsou odstraněna písmena I, L, O a U, aby nedocházelo k záměně s jinými znaky.

Jak se ULID liší od UUID? ULID se řadí podle času vytvoření a má délku 26 znaků. Náhodný UUID (verze 4) se podle času neřadí a má délku 36 znaků včetně spojovníků.

Jak pravděpodobná je kolize mezi dvěma ULIDy? 80 bitů náhodnosti činí kolize extrémně nepravděpodobnými. K dosažení pravděpodobnosti 50 %, že se kterékoli dva shodují, by bylo nutné vygenerovat během stejné milisekundy přibližně 1,3 bilionu ULIDů.

Lze ULIDy generovat bez připojení k internetu? Ano. ULID potřebuje pouze místní hodiny a generátor náhodných čísel, takže jej lze vytvořit zcela offline.

Reference

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