Przejdź do treści

Generator ULID - Twórz Unikalne, Sortowalne ID Online Bezpłatnie

Bezpłatne narzędzie do generowania ULID tworzy unikalne, sortowalne identyfikatory natychmiastowo. Generuj kryptograficznie bezpieczne ULID dla baz danych, API i systemów rozproszonych online.

Generator ULID

Wprowadź wartość, aby zobaczyć wynik
Kalkulator załadunku...
📚

Dokumentacja

Czym jest ULID?

ULID (Universally Unique Lexicographically Sortable Identifier, czyli uniwersalnie unikatowy identyfikator sortowalny leksykograficznie) to kod o długości 26 znaków, używany do oznaczania rekordów w bazie danych lub zdarzeń w systemie rozproszonym. Łączy bieżący czas z losowymi danymi, dlatego lista ULID-ów posortowana alfabetycznie zostaje uporządkowana według czasu utworzenia każdego z nich. Generator ULID to narzędzie tworzące te kody.

Struktura ULID

ULID składa się z dwóch części zapisywanych kolejno, bez separatora:

  • Znacznik czasu (10 znaków): liczba milisekund od 1 stycznia 1970 (epoki Uniksa), zajmująca maksymalnie 48 bitów.
  • Losowość (16 znaków): 80 bitów losowych danych dodawanych po to, aby dwa identyfikatory ULID utworzone w tej samej milisekundzie niemal na pewno się różniły.

Obie części są zapisywane za pomocą alfabetu Base32 Crockforda: cyfr 0–9 oraz liter A–Z z pominięciem liter I, L, O i U. Te cztery litery pominięto, ponieważ można je pomylić z cyframi 0 i 1 albo ze sobą nawzajem. W rezultacie powstaje ciąg, który jest krótki, łatwy do odczytania na głos i można go bezpośrednio umieszczać w adresie URL.

Jak obliczyć ULID

Generator ULID tworzy identyfikator w trzech krokach.

  1. Odczytaj bieżący czas w milisekundach od epoki Uniksa. Jest to liczba 48-bitowa.
  2. Wygeneruj 80 bitów losowych danych, zwykle za pomocą kryptograficznie bezpiecznego generatora liczb losowych, uzyskując 10 losowych bajtów.
  3. Zakoduj obie części osobno za pomocą alfabetu Base32 Crockforda: znacznik czasu o długości 48 bitów staje się ciągiem 10 znaków, a losowa wartość o długości 80 bitów staje się ciągiem 16 znaków. Oba zakodowane ciągi są łączone w końcowy ULID o długości 26 znaków.

Kodowanie polega na odczytywaniu liczby po 5 bitów, ponieważ każdy znak Base32 reprezentuje jedną z 32 możliwych wartości (2^5 = 32). Dziesięć znaków przechowuje maksymalnie 50 bitów, co wystarcza na znacznik czasu o długości 48 bitów. Szesnaście znaków przechowuje dokładnie 80 bitów, czyli tyle, ile zajmuje część losowa, bez pozostałych bitów.

Przykład obliczeniowy

Załóżmy, że generator ULID działa przy znaczniku czasu 1712345678901 (milisekund od epoki) i pobiera losowe bajty [12, 240, 88, 3, 199, 45, 6, 231, 128, 17].

Krok 1 – zakoduj znacznik czasu. Wielokrotnie dziel 1712345678901 przez 32, za każdym razem zapisując resztę i odczytując cyfry od końca do początku (jest to standardowa konwersja systemu liczbowego). Powstaje ciąg o długości 10 znaków: 01HTQW311N.

Krok 2 – zakoduj losowość. Połącz 10 losowych bajtów w strumień 80 bitów, a następnie odczytuj po 5 bitów i zamieniaj każdy fragment 5 bitów na znak Base32. Powstaje ciąg o długości 16 znaków: 1KR5G0Y75M3EF00H.

Krok 3 – połącz części. Końcowy ULID to:

101HTQW311N 1KR5G0Y75M3EF00H
2

zapis bez spacji: 01HTQW311N1KR5G0Y75M3EF00H, łącznie 26 znaków.

Korzystanie z tego generatora

Generator działa w przeglądarce. Każdy ULID jest tworzony na podstawie zegara przeglądarki oraz 80 nowych losowych bitów pobranych z kryptograficznego generatora liczb losowych przeglądarki, dlatego żaden identyfikator nie jest wysyłany do serwera ani z niego pobierany.

  • Liczba ULID-ów przyjmuje liczbę całkowitą od 1 do 50. Wartość domyślna to 1.
  • Generuj ULID tworzy nową partię identyfikatorów. Zmiana liczby również tworzy nową partię.
  • Przycisk Kopiuj przy wyniku kopiuje pojedynczy ULID. Gdy wyświetlony jest więcej niż jeden identyfikator, przycisk Kopiuj wszystko kopiuje całą listę, po jednym ULID-zie w wierszu.
  • Resetuj przywraca liczbę 1 i generuje jeden nowy ULID.

Pod wynikami panel Struktura ULID dzieli pierwszy ULID z partii na dwie części: pierwsze 10 znaków (znacznik czasu) oraz ostatnie 16 znaków (losowość).

ULID a UUID

CechaULIDUUID (wersja 4)
Długość26 znaków36 znaków (z łącznikami)
Możliwość sortowania według czasu utworzeniaTakNie
KodowanieBase32 CrockfordaSzesnastkowy
Zawiera znacznik czasuTak (48 bitów)Nie
Bezpieczny w adresach URL bez kodowania znaków specjalnychTakTak

Oba formaty mają służyć do tworzenia identyfikatorów unikatowych w różnych systemach, bez centralnego podmiotu przydzielającego numery. Główna praktyczna różnica dotyczy sortowania: ponieważ ULID zaczyna się od znacznika czasu, sortowanie ULID-ów jako zwykłego tekstu porządkuje je również według czasu. Losowy UUID nie ma tej właściwości, ponieważ każda jego część jest losowa.

Typowe zastosowania

  • Klucze główne baz danych: ULID-y pozwalają bazie danych indeksować nowe wiersze w przybliżonej kolejności ich wstawiania, co wiele silników baz danych obsługuje wydajniej niż całkowicie losowe klucze.
  • Systemy rozproszone: poszczególne serwery mogą niezależnie generować ULID-y bez uzgadniania ich z centralnym licznikiem, a wyniki nadal są niemal na pewno unikatowe.
  • Dzienniki zdarzeń: ponieważ ULID-y sortują się według czasu, lista identyfikatorów zdarzeń może pełnić również funkcję przybliżonej osi czasu.
  • Identyfikatory API i plików: stały format o długości 26 znaków, bezpieczny w adresach URL, sprawdza się w adresach internetowych i nazwach plików.

Powiązane formaty identyfikatorów to KSUID, który również koduje znacznik czasu na potrzeby sortowania, oraz identyfikatory Snowflake używane przez Twittera i inne firmy, łączące znacznik czasu, identyfikator maszyny i licznik.

Przykłady kodu

Każdy z poniższych przykładów tworzy ULID w taki sam sposób, jaki opisuje specyfikacja ULID: koduje znacznik czasu o długości 48 bitów jako 10 znaków Base32 Crockforda, koduje 80 bitów losowości jako kolejnych 16 znaków, a następnie łączy obie części.

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

Często zadawane pytania

Do czego służy ULID? ULID służy jako unikatowy identyfikator wiersza w bazie danych, zasobu API lub zarejestrowanego zdarzenia, gdy przydatne jest również sortowanie według czasu utworzenia.

Jak długi jest ULID? ULID zawsze ma długość 26 znaków: 10 znaków przypada na znacznik czasu, a 16 na część losową.

Jakiego kodowania używa ULID? ULID używa alfabetu Base32 Crockforda, złożonego z cyfr 0–9 oraz liter A–Z z usuniętymi literami I, L, O i U, aby uniknąć pomyłek z innymi znakami.

Czym ULID różni się od UUID? ULID sortuje się według czasu utworzenia i ma długość 26 znaków. Losowy UUID (wersja 4) nie sortuje się według czasu i ma długość 36 znaków, wraz z łącznikami.

Jakie jest prawdopodobieństwo kolizji między dwoma ULID-ami? 80 bitów losowości sprawia, że kolizje są niezwykle mało prawdopodobne. Aby osiągnąć 50% prawdopodobieństwo zgodności dowolnych dwóch identyfikatorów, trzeba wygenerować około 1,3 biliona ULID-ów w tej samej milisekundzie.

Czy ULID-y można generować bez połączenia z internetem? Tak. ULID wymaga tylko lokalnego zegara i generatora liczb losowych, więc można go utworzyć całkowicie offline.

Piśmiennictwo

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