Генератор ULID: унікальні, сортовані за часом ідентифікатори
Що таке ULID і чому він відрізняється від інших форматів
ULID (Universally Unique Lexicographically Sortable Identifier) — це 128-бітний ідентифікатор, що кодується у 26 символів за допомогою алфавіту Крокфорда base32. На відміну від UUID, який має 36 символів з дефісами, ULID не містить жодних розділових знаків, є нечутливим до регістру та безпечним для використання в URL без екранування. Перші 10 символів ULID кодують час створення з точністю до мілісекунди — це робить ідентифікатори лексикографічно сортованими, тобто вони природним чином впорядковуються за часом у звичайному алфавітному сортуванні.
Сторінка ulid-generator пропонує саме цей формат як одну з опцій. Користувач обирає ULID, задає кількість ідентифікаторів (від 1 до 100) і миттєво отримує список готових рядків. Жодних перемикачів для регістру чи дефісів не потрібно — ULID за визначенням нечутливий до регістру і не має дефісів. Це спрощує інтерфейс і зменшує ризик помилок при копіюванні.
Внутрішня будова ULID: як 128 біт перетворюються на 26 символів
ULID використовує всі 128 біт, але розподіляє їх інакше, ніж UUID v4. Стандартна схема:
- 48 біт — часова мітка (timestamp) у мілісекундах від початку епохи Unix (1 січня 1970 року). Це дозволяє однозначно кодувати дати до 10895 року.
- 80 біт — випадкова компонента, згенерована криптографічно стійким генератором випадкових чисел.
Весь 128-бітний блок кодується за допомогою алфавіту base32 Крокфорда. Цей алфавіт містить символи 0123456789ABCDEFGHJKMNPQRSTVWXYZ — спеціально видалені літери I, L, O, U, які можна сплутати з цифрами 1, 0, або одна з одною. Регістр не має значення: a і A розпізнаються однаково, тому під час копіювання не виникає проблем із верхнім/нижнім регістром.
Перші 10 символів (48 біт) відповідають за час. На практиці це означає, що якщо взяти два ULID, створені з інтервалом хоча б у 1 мілісекунду, то той, що згенеровано пізніше, матиме більший лексикографічний порядок. Решта 16 символів (80 біт) — випадковий суфікс, який забезпечує унікальність навіть при одночасному створенні багатьох ідентифікаторів.
Порівняння з UUID: довжина, сортованість, зручність
UUID v4 (найпоширеніший) має 128 біт випадкових даних, але кодується у 36 символів з дефісами через кожні 8‑4‑4‑4‑12 символів. ULID зберігає той самий об'єм інформації в 26 символах без дефісів. Для баз даних і API, де довжина рядка має значення, ULID дає економію ~28% місця.
UUID v7 — новіший стандарт, який також додає часову мітку, але ULID з’явився раніше і вже широко використовується. Ключові відмінності:
- ULID використовує алфавіт base32 (без дефісів), UUID — шістнадцятковий base16 (з дефісами).
- ULID — нечутливий до регістру, UUID — чутливий (хоча на практиці часто пишуть малими).
- ULID має фіксований порядок символів: час потім випадкова частина. UUID v7 розподіляє біти інакше.
Важливо: ULID гарантує сортування за часом лише для ідентифікаторів, створених у різні мілісекунди. В межах однієї мілісекунди порядок визначається випадковою компонентою, тому суворої хронології для всіх ID очікувати не варто.
Сортування та продуктивність у базах даних
Одна з головних причин використовувати ULID — це покращення продуктивності B-дерев у базах даних, наприклад у PostgreSQL, MySQL чи SQLite. Коли первинний ключ є випадковим UUID v4, нові записи потрапляють у випадкові сторінки індексу, що спричинює часте пересортування сторінок (page splits). ULID же є монотонно зростаючим (загалом): нові ID зазвичай мають більше значення, ніж попередні, тому нові рядки додаються в кінець індексу. Це зменшує фрагментацію та покращує швидкість вставки.
Проте режим одночасної генерації великої кількості ID (наприклад, пакетне вставляння в одній транзакції) може створювати ID в межах однієї мілісекунди — тоді вони матимуть однаковий префікс, але різний випадковий суфікс. Для дуже високих навантажень (мільйони записів на секунду) можна розглянути варіанти з монотонним лічильником у межах мілісекунди, але стандартний ULID цього не передбачає.
Імовірність колізій: наскільки унікальний ULID?
Випадкова компонента ULID — 80 біт. Це означає 2^80 ≈ 1.2 × 10^24 можливих значень. Для порівняння: UUID v4 має 122 випадкові біти (оскільки 6 біт зарезервовано під версію та варіант), тому простір у 2^122 ≈ 5.3 × 10^36. ULID має менший випадковий простір, але для більшості практичних застосувань його цілком достатньо.
Якщо генерувати 1 мільйон ULID на секунду, то очікуваний час до першої колізії (за формулою дня народження) становить приблизно sqrt(π/2 * 2^80) / 10^6 секунд — це близько 10^7 років. Навіть за 10 мільйонів ID на секунду ризик лишається вкрай малим.
Генерація на цій сторінці використовує crypto.getRandomValues() — криптографічно стійкий генератор випадкових чисел, доступний у всіх сучасних браузерах. Жодні дані не передаються на сервер; весь процес відбувається локально.
Безпека в URL та людська читабельність
ULID складається лише з символів, дозволених в URI без екранування (нерезервовані символи згідно з RFC 3986: літери, цифри, дефіс, крапка, підкреслення, тильда). Дефіс у ULID не використовується, тому жодного екранування не потрібно.
Відсутність літер I, L, O, U також зменшує візуальні помилки при ручному введенні. Наприклад, рядок 3AR72X6Y0Z (без неоднозначних символів) легше переписати, ніж 3AR7I2X6Y0Z (де I може сплутатися з 1). Для коротких посилань або токенів це вагома перевага.
Як користуватися генератором: вхідні та вихідні дані
Сторінка пропонує простий інтерфейс:
- Формат — обирається ULID (інші формати — UUID, NanoID тощо — доступні на інших вкладках).
- Кількість — ціле число від 1 до 100 включно.
- Кнопка генерації — запускає створення списку.
Після генерації з’являється список ULID, а зверху — індикатор кількості згенерованих ID та статус: "Готово." (Ready), "Згенеровано." (Generated) або "Скопійовано все!" (Copied all!). Кожен окремий ULID можна скопіювати одним кліком; кнопка "Копіювати все" копіює весь список одразу. Зміна будь-якого параметра (формату чи кількості) автоматично перегенеровує список.
FAQ
1. Чи можна використовувати ULID як первинний ключ у базі даних?
Так. ULID добре підходить для первинних ключів, особливо в таблицях з частими вставками, оскільки його часова компонента сприяє лінійному зростанню індексу. Однак слід пам’ятати, що в межах однієї мілісекунди порядок випадковий, тому для випадків, де потрібна строга хронологія навіть у межах мілісекунди, краще використовувати монотонні лічильники (наприклад, серійні номери).
2. Чи генерує сторінка ULID з великими чи малими літерами?
ULID нечутливий до регістру, тому сторінка показує рядки у верхньому регістрі (алфавіт Крокфорда зазвичай подають великими літерами). При копіюванні ви отримуєте саме той регістр, який бачите. Якщо ваша система приймає лише малі літери, просто переведіть рядок до нижнього регістру — це не вплине на унікальність.
3. Чому ULID має саме 26 символів?
128 біт ділиться на 5 біт на символ (оскільки base32 кодує 5 біт одним символом). 128 / 5 = 25.6, тому потрібен 26-й символ для заповнення останніх 3 біт (зазвичай нульові). Це стандартне округлення вгору.
4. Яка точність часової мітки в ULID?
Мілісекундна точність (48 біт). Це означає, що ULID, створені з різницею в 1 мс, будуть лексикографічно впорядковані. ID, створені в одну і ту ж мілісекунду, матимуть однаковий префікс, але різний випадковий суфікс — порядок між ними не гарантовано.
5. Чи можна згенерувати більше 100 ULID одночасно?
Ні, обмеження сторінки — максимум 100 ID. Якщо потрібна більша кількість, можна зробити кілька послідовних генерацій. Причина обмеження — уникнення перевантаження браузера та зручність копіювання.
6. Чи зберігаються згенеровані ULID на сервері?
Ні. Вся генерація відбувається локально у вашому браузері. Ніякі дані не надсилаються на сервер. Після оновлення сторінки список зникає; ви можете зберегти його лише скопіювавши.