Перейти к содержимому

ULID-генератор - Создание уникальных сортируемых ID онлайн бесплатно

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

Генератор ULID

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

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

Что такое ULID?

ULID (Universally Unique Lexicographically Sortable Identifier — универсальный уникальный лексикографически сортируемый идентификатор) — это код из 26 символов, используемый для обозначения записей в базе данных или событий в распределённой системе. Он объединяет текущее время со случайными данными, поэтому список ULID, отсортированный по алфавиту, оказывается отсортированным по времени создания каждого идентификатора. Генератор ULID — это инструмент, создающий такие коды.

Структура ULID

ULID состоит из двух частей, записанных последовательно без разделителя:

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

Обе части записываются с использованием алфавита Base32 Крокфорда: цифр 0–9 и букв A–Z, кроме I, L, O и U. Эти четыре буквы исключены, поскольку их можно перепутать с цифрами 0 и 1 или друг с другом. В результате получается короткая строка, которую легко читать вслух и безопасно помещать непосредственно в URL.

Как вычислить ULID

Генератор ULID создаёт идентификатор в три этапа.

  1. Считать текущее время в миллисекундах с начала эпохи Unix. Это число размером 48 бит.
  2. Сгенерировать 80 бит случайных данных, обычно с помощью криптографически безопасного генератора случайных чисел, получив 10 случайных байт.
  3. Закодировать обе части отдельно с использованием алфавита 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 символов (с дефисами)
Сортировка по времени созданияДаНет
КодированиеBase32 КрокфордаШестнадцатеричное
Содержит метку времениДа (48 бита)Нет
Безопасен для URL без экранированияДаДа

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

Распространённые применения

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

К связанным форматам идентификаторов относятся KSUID, который также кодирует метку времени для сортировки, и идентификаторы Snowflake, используемые Twitter и другими компаниями; они объединяют метку времени, идентификатор машины и счётчик.

Примеры кода

Каждый приведённый ниже пример создаёт ULID тем же способом, который описан в спецификации ULID: кодирует метку времени размером 48 бит в 10 символов 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 использует алфавит Base32 Крокфорда, состоящий из цифр 0–9 и букв A–Z, из которого удалены I, L, O и U, чтобы избежать путаницы с другими символами.

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

Насколько вероятно совпадение двух ULID? 80 бит случайных данных делают совпадения крайне маловероятными. Чтобы вероятность совпадения любых двух идентификаторов достигла 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.