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 создаёт идентификатор в три этапа.
- Считать текущее время в миллисекундах с начала эпохи Unix. Это число размером 48 бит.
- Сгенерировать 80 бит случайных данных, обычно с помощью криптографически безопасного генератора случайных чисел, получив 10 случайных байт.
- Закодировать обе части отдельно с использованием алфавита 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
| Характеристика | ULID | UUID (версия 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());
33Python
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())
32Java
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 нужны только локальные часы и генератор случайных чисел, поэтому его можно полностью создать в автономном режиме.
Источники
- "ULID Specification." GitHub, https://github.com/ulid/spec.
- "Crockford's Base32 Encoding." http://www.crockford.com/base32.html.