Перейти до вмісту

Генератор ULID - Створення унікальних сортованих ідентифікаторів онлайн безкоштовно

Безкоштовний інструмент генерації ULID створює унікальні, сортовані ідентифікатори миттєво. Генеруйте криптографічно безпечні ULID для баз даних, API та розподілених систем онлайн.

Генератор ULID

Введіть значення для перегляду результатів
Калькулятор завантаження...
📚

Документація

Що таке ULID?

ULID (універсально унікальний ідентифікатор, придатний для лексикографічного сортування) — це код із 26 символів, який використовують для позначення записів у базі даних або подій у розподіленій системі. Він поєднує поточний час із випадковими даними, тому список ULID, відсортований за алфавітом, зрештою буде впорядкований за часом створення кожного ідентифікатора. Генератор ULID — це інструмент, який створює такі коди.

Структура ULID

ULID має дві частини, записані одна за одною без роздільника:

  • Мітка часу (10 символів): кількість мілісекунд від 1 січня 1970 р. (епохи Unix), що займає до 48 бітів.
  • Випадкова частина (16 символів): 80 бітів випадкових даних, доданих для того, щоб два ULID, створені в одну мілісекунду, майже напевно відрізнялися.

Обидві частини записують за допомогою алфавіту Crockford Base32: цифри 0–9 і літери A–Z, за винятком I, L, O та U. Ці чотири літери не використовують, оскільки їх можна сплутати з цифрами 0 і 1 або між собою. У результаті отримують короткий рядок, який легко прочитати вголос і безпечно безпосередньо вставляти в URL.

Як обчислити ULID

Генератор ULID створює ідентифікатор у три етапи.

  1. Зчитати поточний час у мілісекундах від епохи Unix. Це число завдовжки 48 бітів.
  2. Згенерувати 80 бітів випадкових даних, зазвичай за допомогою криптографічно стійкого генератора випадкових чисел, отримавши 10 випадкових байтів.
  3. Окремо закодувати обидві частини за допомогою алфавіту Crockford Base32: мітка часу завдовжки 48 бітів перетворюється на 10 символів, а випадкове значення завдовжки 80 бітів — на 16 символів. Два закодовані рядки об’єднують, щоб отримати кінцевий ULID завдовжки 26 символів.

Кодування працює шляхом зчитування числа по 5 бітів за раз, оскільки кожен символ Base32 представляє одне з 32 можливих значень (2^5 = 32). Десять символів містять до 50 бітів, чого достатньо для мітки часу завдовжки 48 бітів. Шістнадцять символів містять рівно 80 бітів, що відповідає частині випадкових даних без залишку.

Приклад обчислення

Припустімо, генератор ULID працює з міткою часу 1712345678901 (мілісекунди від епохи) і отримує випадкові байти [12, 240, 88, 3, 199, 45, 6, 231, 128, 17].

Крок 1 — кодування мітки часу. Послідовно ділимо 1712345678901 на 32, щоразу беручи остачу й зчитуючи цифри від останньої до першої (це стандартне переведення між системами числення). У результаті отримуємо рядок із 10 символів 01HTQW311N.

Крок 2 — кодування випадкової частини. Об’єднуємо 10 випадкових байтів у потік із 80 бітів, потім зчитуємо їх по 5 бітів за раз, перетворюючи кожну групу з 5 бітів на символ Base32. У результаті отримуємо рядок із 16 символів 1KR5G0Y75M3EF00H.

Крок 3 — об’єднання частин. Кінцевий ULID:

101HTQW311N 1KR5G0Y75M3EF00H
2

записаний без пробілу: 01HTQW311N1KR5G0Y75M3EF00H, загалом 26 символів.

Використання цього генератора

Генератор працює у браузері. Кожен ULID створюється на основі годинника браузера та 80 свіжих випадкових бітів, отриманих від криптографічного генератора випадкових чисел браузера, тому жоден ідентифікатор не надсилається на сервер і не завантажується з нього.

  • Кількість ULID приймає ціле число від 1 до 50. Значення за замовчуванням — 1.
  • Згенерувати ULID створює нову групу. Зміна кількості також створює нову групу.
  • Кнопка Копіювати біля результату копіює лише цей ULID. Якщо показано більше одного, кнопка Копіювати все копіює весь список, по одному ULID у рядку.
  • Скинути повертає кількість до 1 і створює один новий ULID.

Під результатами панель «Структура ULID» розділяє перший ULID у групі на дві частини: перші 10 символів (мітка часу) та останні 16 (випадкова частина).

ULID і UUID

ХарактеристикаULIDUUID (версія 4)
Довжина26 символів36 символів (із дефісами)
Сортування за часом створенняТакНі
КодуванняCrockford Base32Шістнадцяткова
Містить мітку часуТак (48 бітів)Ні
Безпечний для URL без екрануванняТакТак

Обидва формати призначені для створення ідентифікаторів, унікальних у різних системах, без центрального органу, який розподіляє номери. Головна практична відмінність полягає в сортуванні: оскільки ULID починається з мітки часу, сортування ULID як звичайного тексту також упорядковує їх за часом. Випадковий UUID не має цієї властивості, оскільки кожна його частина є випадковою.

Поширені способи використання

  • Первинні ключі бази даних: ULID дають змогу індексувати нові рядки приблизно в порядку їх вставлення, із чим багато рушіїв баз даних працюють ефективніше, ніж із повністю випадковими ключами.
  • Розподілені системи: окремі сервери можуть незалежно створювати ULID без узгодження з центральним лічильником, і результати майже напевно залишатимуться унікальними.
  • Журнали подій: оскільки ULID сортуються за часом, список ідентифікаторів подій може слугувати приблизною часовою шкалою.
  • Ідентифікатори API та файлів: фіксований формат завдовжки 26 символів, безпечний для URL, коректно працює у вебадресах та іменах файлів.

До споріднених форматів ідентифікаторів належать KSUID, який також кодує мітку часу для сортування, і ідентифікатори Snowflake, що використовуються Twitter та іншими сервісами й поєднують мітку часу, ідентифікатор машини та лічильник.

Приклади коду

Кожен наведений нижче приклад створює ULID так само, як описано в специфікації ULID: кодує мітку часу завдовжки 48 бітів у 10 символів Crockford Base32, кодує 80 бітів випадкових даних ще у 16 символів і об’єднує їх.

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

Поширені запитання

Для чого використовують ULID? ULID використовують як унікальний ідентифікатор рядка бази даних, ресурсу API або записаної події, коли також важливо сортувати їх за часом створення.

Яка довжина ULID? ULID завжди має довжину 26 символів: 10 символів для мітки часу та 16 для випадкової частини.

Яке кодування використовує ULID? ULID використовує алфавіт Crockford Base32, що складається з цифр 0–9 і літер A–Z, за винятком I, L, O та U, щоб уникнути плутанини з іншими символами.

Чим ULID відрізняється від UUID? ULID сортується за часом створення і має довжину 26 символів. Випадковий UUID (версія 4) не сортується за часом і має довжину 36 символів, включно з дефісами.

Наскільки ймовірне зіткнення між двома ULID? 80 бітів випадкових даних роблять збіги надзвичайно малоймовірними. Щоб імовірність збігу будь-яких двох ULID досягла 50%, потрібно згенерувати приблизно 1,3 трильйона ULID протягом однієї мілісекунди.

Чи можна генерувати ULID без підключення до інтернету? Так. ULID потребує лише локального годинника та генератора випадкових чисел, тому його можна повністю створювати в автономному режимі.

Джерела

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