UUID: 128-битная структура, версии и реальные сценарии применения
Каждый раз, когда вы регистрируетесь в сервисе, для вашего аккаунта создаётся уникальный идентификатор. Каждый запрос API несёт trace ID. Каждой строке в распределённой базе данных нужен первичный ключ, который не столкнётся с ключами, сгенерированными на других машинах. Решение, стоящее за всем этим? UUID — Universally Unique Identifier (универсально уникальный идентификатор).
Это руководство объясняет, что такое UUID, как они устроены, что делает каждая версия под капотом и когда их стоит (или не стоит) использовать.
UUID кратко
UUID — это 128-битный (16-байтовый) идентификатор, разработанный для глобальной уникальности без необходимости центрального авторитета. Записывается как 32 шестнадцатеричных цифры в каноническом формате 8-4-4-4-12:
550e8400-e29b-41d4-a716-446655440000
|------| |--| |--| |--| |----------|
8 hex 4 4 4 12 hex
Итого 32 hex-символа + 4 дефиса = 36 символов. Дефисы носят чисто косметический характер — они не несут данных.
Ключевые факты:
- 128 бит = 2¹²⁸ ≈ 3,4 × 10³⁸ возможных значений
- Стандартизован документом RFC 9562 (май 2024 года, заменяет RFC 4122)
- В экосистемах Microsoft также называется GUID (Globally Unique Identifier) — тот же формат, другое имя
- Поддерживается нативно в PostgreSQL (тип
uuid), MySQL (BINARY(16)илиCHAR(36)) и практически в каждом языке программирования
Анатомия UUID
В каждом UUID, независимо от версии, два метаданных-поля занимают фиксированные позиции битов:
550e8400-e29b-41d4-a716-446655440000
^ ^
| |
Version-┘ └-Variant
Поле версии (биты 48–51)
13-я hex-цифра (первая цифра третьей группы) определяет версию UUID:
| Hex-цифра | Версия | Метод |
|---|---|---|
1 | v1 | Timestamp + MAC-адрес |
3 | v3 | MD5-хеш пространства имён + имя |
4 | v4 | Криптографически случайный |
5 | v5 | SHA-1-хеш пространства имён + имя |
6 | v6 | Переупорядоченный timestamp (RFC 9562) |
7 | v7 | Unix timestamp + случайные биты (RFC 9562) |
8 | v8 | Пользовательский / специфичный для реализации |
Поле варианта (биты 64–65)
17-я hex-цифра (первая цифра четвёртой группы) определяет вариант. Для UUID по RFC 4122/9562 первые биты равны 10, поэтому эта цифра всегда 8, 9, a или b.
Разбор примера
550e8400-e29b-41d4-a716-446655440000
↑ ↑
4 → v4 a → вариант RFC 4122
Это UUID v4 (случайный), вариант RFC 4122/9562.
Версии UUID подробно
Версия 1: timestamp + MAC-адрес
UUID v1 был исходным дизайном. Он кодирует:
- 60-битный timestamp — интервалы по 100 наносекунд с 15 октября 1582 года (григорианская календарная реформа)
- 14-битная последовательность часов — счётчик монотонности для предотвращения дубликатов при откате часов
- 48-битный node — обычно MAC-адрес машины
| Timestamp | Ver | Clk |Var| Node (MAC) |
| 60 bits | 4b | 14b |2b | 48 bits |
Проблемы:
- Раскрывает время генерации и идентичность оборудования (риск приватности)
- MAC-адреса можно подделать, что подрывает уникальность
- Эпоха 1582 года сбивает с толку и требует конвертации
Вердикт: считается устаревшей по RFC 9562. Для UUID на основе времени используйте v7.
Версия 3: на основе имени с MD5 (детерминированная)
UUID v3 вычисляет хеш по UUID пространства имён и строке имени с помощью MD5. Одни и те же входные данные всегда дают один и тот же UUID.
import uuid
# namespace = DNS, name = "example.com"
print(uuid.uuid3(uuid.NAMESPACE_DNS, "example.com"))
# → "9073926b-929f-31c2-abc9-fad77ae3e8eb" (всегда это значение)
Определены четыре стандартных пространства имён:
- DNS:
6ba7b810-9dad-11d1-80b4-00c04fd430c8 - URL:
6ba7b811-9dad-11d1-80b4-00c04fd430c8 - OID:
6ba7b812-9dad-11d1-80b4-00c04fd430c8 - X.500:
6ba7b814-9dad-11d1-80b4-00c04fd430c8
Вердикт: работает, но v5 предпочтительнее — SHA-1 надёжнее MD5.
Версия 4: случайный — самая популярная
UUID v4 заполняет 122 бита криптографически безопасными случайными данными (оставшиеся 6 бит зарезервированы под поля версии и варианта).
| Random | Ver | Random |Var| Random |
| 48 bits | 4b | 12 bits |2b | 62 bits |
При 2¹²² ≈ 5,3 × 10³⁶ возможных значений вероятность коллизии астрономически мала. Чтобы шанс хотя бы одной коллизии достиг 50 %, потребуется около 2,71 × 10¹⁸ UUID — это 2,71 квинтиллиона.
// Поддерживается во всех современных браузерах и в Node.js
const id = crypto.randomUUID();
console.log(id); // → "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
Сильные стороны: простота, приватность, повсеместная поддержка, не нужна координация.
Слабая сторона: случайное распределение вызывает фрагментацию B-tree-индекса, когда v4 используется как первичный ключ. Для нагруженных сценариев работы с базой рассмотрите v7.
Версия 5: на основе имени с SHA-1 (детерминированная)
Идентична v3, но использует SHA-1 вместо MD5. Одни и те же входные данные всегда дают один и тот же UUID.
import uuid
print(uuid.uuid5(uuid.NAMESPACE_DNS, "example.com"))
# → "cfbff0d1-9375-5685-968c-48ce8b15ae17" (всегда это значение)
Сценарии применения:
- Генерация стабильных идентификаторов из URL или DNS-имён
- Ключи контентно-адресуемого хранилища
- Воспроизводимые тестовые фикстуры
Важно: v3 и v5 НЕ предназначены для безопасности. Они детерминированы — любой, кто знает пространство имён и имя, может воспроизвести UUID.
Версия 7: Unix timestamp + случайные биты (рекомендуется для новых проектов)
UUID v7 — самая новая версия, представлена в RFC 9562 (май 2024 года). Она кодирует:
- 48-битный Unix timestamp в миллисекундах — монотонно возрастает
- 74 бита криптографической случайности
| Unix timestamp (ms) | Ver | rand_a |Var| rand_b |
| 48 bits | 4b | 12 bits |2b | 62 bits |
Это означает, что UUID v7 естественным образом сортируются по времени создания — новые UUID лексикографически больше старых. Это свойство делает их идеальными для первичных ключей баз данных: B-tree-индексы остаются последовательными, а не фрагментируются случайно.
import { v7 as uuidv7 } from "uuid";
const id1 = uuidv7(); // сгенерирован в момент T₁
const id2 = uuidv7(); // сгенерирован в момент T₂ (T₂ > T₁)
console.log(id1 < id2); // → true (лексикографическое сравнение)
Почему это важно для баз данных: последовательное свойство v7 уменьшает количество разбиений страниц индекса (page splits) до 90 % по сравнению с v4, что даёт более быстрые вставки, меньшие индексы и лучшее использование кеша.
UUID и GUID — в чём разница?
Функциональной разницы нет. GUID (Globally Unique Identifier) — это название UUID в Microsoft, используемое в Windows, .NET, COM и SQL Server. Формат идентичен: 128 бит, hex 8-4-4-4-12.
Единственное косметическое отличие: инструменты Microsoft иногда отображают GUID в верхнем регистре с фигурными скобками:
UUID: 550e8400-e29b-41d4-a716-446655440000
GUID: {550E8400-E29B-41D4-A716-446655440000}
Если кто-то спрашивает «в чём разница между UUID и GUID», ответ: брендинг.
Специальные значения UUID
RFC 9562 определяет два особых UUID:
| Имя | Значение | Назначение |
|---|---|---|
| Nil UUID | 00000000-0000-0000-0000-000000000000 | Обозначает отсутствие значения (как null) |
| Max UUID | ffffffff-ffff-ffff-ffff-ffffffffffff | Граничный маркер или sentinel-значение |
Никогда не используйте их как настоящие идентификаторы — они не уникальны по определению.
Вероятность коллизии: задача о днях рождения
«Задача о днях рождения» вычисляет, сколько UUID нужно сгенерировать, чтобы коллизия стала вероятной. Для UUID v4 (122 случайных бита):
| Сгенерировано UUID | Вероятность коллизии |
|---|---|
| 1 миллион | ~10⁻²² (практически невозможно) |
| 1 миллиард | ~10⁻¹⁶ (по-прежнему пренебрежимо мало) |
| 2,71 × 10¹⁸ | 50 % («граница дней рождения») |
Для контекста: если генерировать 1 миллиард UUID в секунду, потребуется 86 лет, чтобы вероятность хотя бы одной коллизии достигла 50 %. На практике аппаратный сбой, ошибки в коде и космические лучи скорее приведут к дубликату, чем сама математика UUID v4.
Формула: p(n) ≈ n² / (2 × 2¹²²)
Как валидировать UUID
Корректный UUID соответствует регулярному выражению (без учёта регистра):
^[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
Эта проверка контролирует:
- Формат hex 8-4-4-4-12
- Цифру версии в диапазоне 1–7 (позиция 15)
- Полубайт варианта начинается с 8, 9, a или b (позиция 20)
function isValidUUID(str) {
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(str);
}
isValidUUID("550e8400-e29b-41d4-a716-446655440000"); // → true
isValidUUID("not-a-uuid"); // → false
Генерация UUID на разных языках
JavaScript / TypeScript
// Браузер и Node.js — встроенная v4
crypto.randomUUID();
// npm-пакет uuid — поддерживает v1, v3, v4, v5, v7
import { v4, v7 } from "uuid";
v4(); // случайный
v7(); // упорядоченный по времени
Python
import uuid
uuid.uuid4() # случайный
uuid.uuid5(uuid.NAMESPACE_DNS, "example.com") # детерминированный
# uuid.uuid7() запланирован для Python 3.14+
Go
import "github.com/google/uuid"
uuid.New() // v4 случайный
uuid.Must(uuid.NewV7()) // v7 упорядоченный по времени
Java
import java.util.UUID;
UUID.randomUUID(); // v4 случайный
// UUID v7: используйте com.fasterxml.uuid или java.util.UUID в JDK 21+
SQL (PostgreSQL)
-- v4 (PostgreSQL 13+)
SELECT gen_random_uuid();
-- v7 (PostgreSQL 18+)
SELECT uuidv7();
Типичные сценарии применения
Первичные ключи базы данных
UUID позволяют генерировать идентификаторы где угодно — в приложении, на клиенте, на edge-узле — без обращения к базе. Это даёт offline-first-архитектуру и упрощает распределённые системы. Для лучшей производительности индекса используйте v7 или v4, если порядок не важен.
Трассировка запросов API
Назначайте UUID каждому запросу API на входе (gateway, балансировщик нагрузки). Передавайте его всем нижестоящим сервисам через header вроде X-Request-ID. Это упрощает корреляцию логов между микросервисами.
Ключи идемпотентности
В API используют UUID как ключи идемпотентности, чтобы повторные запросы не создавали дублирующиеся ресурсы. Клиент генерирует UUID до первой попытки и отправляет один и тот же UUID при повторах.
Идентификаторы сессий
UUID обеспечивают достаточную уникальность для предотвращения коллизий сессий при большой пользовательской базе. В отличие от auto-increment-целых чисел, их нельзя перебрать — атакующий не угадает валидные идентификаторы сессий простым инкрементом.
Контентно-адресуемое хранилище
UUID v5 генерирует детерминированные идентификаторы из содержимого. На один и тот же вход всегда получается один и тот же UUID — полезно для дедупликации, кеширования и воспроизводимых сборок.
Соображения безопасности
UUID — НЕ токены безопасности
UUID разработаны для уникальности, а не для секретности. Ключевые проблемы:
- UUID v1 раскрывает временную метку генерации и MAC-адрес
- UUID v4 содержит 122 случайных бита, но имеет предсказуемую структуру (биты версии и варианта зафиксированы)
- UUID v3/v5 детерминированы — любой, кто знает пространство имён и имя, может воспроизвести UUID
Для токенов безопасности, ключей API или секретов сессий используйте отдельный CSPRNG со 128 и более битами чистой случайности:
// Для токенов безопасности — это не UUID, а полностью случайные данные
const token = Array.from(crypto.getRandomValues(new Uint8Array(32)))
.map(b => b.toString(16).padStart(2, "0"))
.join("");
UUID v7 раскрывает время создания
Первые 48 бит UUID v7 кодируют timestamp создания в миллисекундах. Любой, кто получит v7-UUID, может извлечь момент его создания:
const hex = "01906b5e-4a3e-7234-8f56-b8c12d4e5678".replace(/-/g, "").slice(0, 12);
new Date(parseInt(hex, 16));
// → 2024-07-01T12:34:56.000Z
Если время создания — чувствительная информация, используйте v4.
Не полагайтесь на UUID для защиты от перебора
Хотя UUID сложнее угадать, чем последовательные целые числа, они не должны быть единственным механизмом контроля доступа. Всегда проверяйте авторизацию — не полагайтесь на «безопасность через скрытность» URL.
Часто задаваемые вопросы
Зачем в UUID дефисы?
Дефисы в формате 8-4-4-4-12 нужны исключительно для удобства чтения человеком. Они не несут данных и игнорируются при разборе. Некоторые системы хранят UUID без дефисов (32 hex-символа), что одинаково корректно.
Могут ли два UUID совпасть?
Теоретически — да, практически — нет. Для UUID v4 со 122 случайными битами вероятность сгенерировать два одинаковых UUID для произвольной пары — около 1 к 5,3 × 10³⁶. На реальных скоростях генерации вас скорее ударит молнией одновременно с выигрышем в лотерею, чем вы встретите коллизию UUID.
UUID последовательны?
Только некоторые версии. UUID v1, v6 и v7 содержат timestamp и сортируются хронологически. UUID v4 полностью случайный, без какого-либо порядка. UUID v3 и v5 детерминированы, но не упорядочены.
Сколько места занимает UUID?
- Бинарно: 16 байт (128 бит) — самое эффективное хранение
- Строка (с дефисами): 36 байт (ASCII)
- Строка (без дефисов): 32 байта (ASCII)
Большинство баз данных хранят UUID внутри в бинарном формате. Нативный тип uuid в PostgreSQL занимает ровно 16 байт.
Что выбрать для первичного ключа — UUID или auto-increment?
Auto-increment проще для приложений с одной базой (меньше, быстрее, последовательно). UUID лучше подходит для распределённых систем (генерация где угодно, без координации, безопасное слияние). Если выбираете UUID, для лучшей производительности базы предпочитайте v7.
Что такое RFC 9562?
RFC 9562, опубликованный в мае 2024 года, — это новейший стандарт UUID. Он заменяет RFC 4122 и формально вводит UUID версий 6, 7 и 8. Он считает v1 устаревшим в пользу v6/v7 и определяет значения nil и max UUID. Если вы реализуете генерацию или валидацию UUID, RFC 9562 — авторитетный источник.
Можно ли использовать UUID между разными языками программирования?
Да. Формат UUID (128 бит, hex 8-4-4-4-12) не зависит от языка. UUID, сгенерированный в JavaScript, корректно прочитается в Python, Go, Java или любом другом языке с поддержкой UUID. Эта совместимость — одно из главных достоинств UUID.
Генерируйте, декодируйте и валидируйте UUID мгновенно с помощью нашего генератора UUID — поддержка v1, v4, v5 и v7, пакетная генерация, 100 % в браузере.
Выбираете между версиями UUID для следующего проекта? См. наше сравнение UUID v4, v7, ULID и Snowflake с практическими бенчмарками базы и примерами кода.