
Что такое UUID и чем отличаются версии v1, v4, v7
ДБ
Дмитрий Борисенко
550e8400-e29b-41d4-a716-446655440000 — если вы работали с базами данных, API
или конфигурацией устройств, такая строка наверняка попадалась на глаза. Это
UUID: идентификатор, который можно сгенерировать где угодно — хоть в браузере,
хоть на сервере без интернета — и почти наверняка не встретить такой же второй
раз. Разберём, как он устроен и какая версия для чего нужна.
UUID и GUID — одно и то же
UUID (Universally Unique Identifier) и GUID (Globally Unique Identifier)
обозначают один и тот же формат — 128-битное число, обычно записанное как 32
шестнадцатеричных цифры через дефисы: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
GUID — это просто более старое название, которое использует Microsoft в своих
технологиях (реестр Windows, .NET, COM-объекты); везде за пределами экосистемы
Microsoft прижилось UUID. Формат идентичный,
генератор UUID одинаково подходит для обеих задач.
Почему UUID почти никогда не повторяется
Дело в размере пространства значений: 128 бит — это 2¹²⁸ возможных комбинаций, астрономическое число. У случайного UUID (версия 4) вероятность столкнуться с уже существующим значением настолько мала, что на практике ей пренебрегают — сравнимо с тем, чтобы дважды подряд угадать один и тот же атом во вселенной. Именно поэтому UUID можно генерировать независимо на разных серверах, в разных базах данных, даже офлайн — без центрального реестра, который бы это отслеживал, как в случае с автоинкрементным ID.
Версии UUID: чем v1, v4 и v7 отличаются
Цифра версии зашита прямо в самом UUID (третья группа символов), и версии не взаимозаменяемы — они по-разному устроены внутри.
- v4 — случайный. Все значащие биты, кроме служебных, заполняются случайными числами. Никакой связи с временем создания или устройством — просто уникальное значение. Самый распространённый вариант по умолчанию, если нет причины выбрать другой.
- v7 — временная метка + случайность. Стандартизирован в 2024 году и быстро набирает популярность: начинается с Unix-времени создания в миллисекундах, а дальше — случайные биты. Из-за этого UUID v7, сгенерированные один за другим, сортируются в том же порядке, в котором были созданы — это удобно для первичного ключа в базе данных: новые записи физически ложатся в конец индекса, а не в случайное место, как с v4.
- v1 — временная метка + идентификатор узла. Старый стандарт: тоже содержит время создания, но вместо случайных битов — идентификатор устройства (изначально это был MAC-адрес сетевой карты). Из-за этой утечки информации об источнике v1 сейчас используют редко — v7 даёт похожую сортируемость без этого недостатка.
- NIL UUID — особый случай, 32 нуля подряд
(
00000000-0000-0000-0000-000000000000). Не идентифицирует ничего — это условная «пустая» заглушка, когда поле обязано быть UUID, а значения ещё нет.
Какую версию выбрать для базы данных
Если UUID — первичный ключ таблицы, вариант по умолчанию v4 создаёт проблему:
случайные значения ложатся в случайные места B-tree-индекса, и это медленнее
последовательной вставки. Version 7 решает именно это — записи растут по
времени, как автоинкремент, но остаются такими же непредсказуемыми и безопасными
для публичных API, где нежелательно, чтобы ID выдавал количество записей в
системе (id=42 очевидно говорит, что записей было мало).
Сгенерировать можно сразу пачку в нужном формате — с
дефисами, без них, в верхнем регистре или в фигурных скобках, как ожидает
конкретная система.
UUID или автоинкремент: что выбрать под первичный ключ
Это главная развилка, ради которой обычно и читают про UUID. Ответ зависит не от вкуса, а от того, где рождаются идентификаторы.
| Что сравниваем | Автоинкремент (int) |
UUID |
|---|---|---|
| Размер | 4 байта (8 у bigint) |
16 байт |
| Кто выдаёт | Только база, по одному | Кто угодно, хоть офлайн |
| Вставка в индекс | Всегда в конец | В конец только у v7 |
| Видно ли масштаб | Да: id=42 выдаёт объём базы |
Нет |
| Слияние двух баз | Конфликт ключей почти гарантирован | Конфликтов не будет |
| Читаемость в логе | Высокая — номер можно продиктовать | Низкая — 36 символов |
Практическое правило простое. Если записи создаёт одна база и наружу ID не светится — автоинкремент, он дешевле по всем статьям. Если идентификатор нужен раньше, чем строка попадёт в базу (клиент сгенерировал заказ офлайн, несколько сервисов пишут в общую таблицу, данные будут сливаться между инсталляциями), — UUID, и именно v7, чтобы не терять на вставке.
Промежуточный вариант, о котором забывают: держать оба. Внутренний bigint как
первичный ключ и связи между таблицами, плюс отдельная колонка с UUID для
внешнего мира — в URL и в публичном API. Так и индексы остаются компактными, и
наружу не утекает количество записей.
Что ломается при переходе с int на UUID
Миграция редко сводится к смене типа колонки — вот что обычно всплывает уже по дороге.
- Внешние ключи. Тип меняется не в одной таблице, а во всех, которые на неё ссылаются. Обычный приём — добавить новую колонку рядом, заполнить её, а переключение связей провести отдельным шагом, не в одной транзакции с заполнением.
- Индексы распухают. Каждый вторичный индекс в PostgreSQL хранит копию первичного ключа, поэтому переход с 4 байт на 16 бьёт не только по самой таблице.
- Сортировка по ID перестаёт быть сортировкой по времени. Запросы вида
ORDER BY id DESCдля «последних записей» на v4 начнут возвращать случайный порядок. Либо переходите на v7, либо явно сортируйте поcreated_at. - Фронтенд и логи. Места, где ID подставлялся в путь или сравнивался как
число, придётся пересмотреть: UUID регистронезависим при сравнении, но строкой
'A1B2'и'a1b2'это два разных значения.
Когда UUID лишний
UUID — не единственный способ получить уникальный идентификатор, и не всегда лучший.
- ULID — те же 128 бит и та же сортируемость по времени, что у v7, но запись
в Crockford Base32: 26 символов вместо 36, без дефисов, и не путается
визуально из-за исключённых
I,L,O,U. Хорош там, где идентификатор часто видит человек. - Snowflake ID (подход Twitter) — 64 бита: время, номер узла и счётчик.
Влезает в
bigint, сортируется по времени, но требует, чтобы у каждого генератора был уникальный номер узла. Это инфраструктура, которую надо поддерживать. - Просто короткий случайный токен. Для ссылки-приглашения или кода доступа
UUID избыточен: там нужна не глобальная уникальность, а непредсказуемость и
ограниченный срок жизни. Уникальность в пределах одной таблицы обеспечит
обычный
UNIQUE-индекс, а стойкость — надёжный пароль нужной длины.
Ещё одна вещь, которую UUID не делает: он не защищает данные. Идентификатор
попадает в адресную строку, историю браузера, логи прокси и заголовок Referer.
Угадать v4 нельзя, но «нельзя угадать» и «нельзя увидеть» — разные свойства, и
секретом такой ID не становится.
Генератор UUID: v4, v7 и v1
Создавайте уникальные идентификаторы UUID — v4, v7 или v1
Попробовать
Коротко
- UUID и GUID — одно и то же, 128-битный уникальный идентификатор; GUID — просто более старое название из мира Microsoft.
- Столкнуться с повторным случайным UUID практически невозможно — пространство значений слишком велико.
- v4 — случайный и универсальный по умолчанию, v7 — с сортируемой временной меткой (лучше для первичных ключей БД), v1 — устаревший вариант с утечкой идентификатора устройства.
- UUID выигрывает у автоинкремента там, где ID рождается вне базы или базы
придётся сливать; в остальных случаях
intдешевле по месту и по индексам. - Часто разумнее держать оба ключа: внутренний
bigintдля связей и UUID для URL и публичного API. - При миграции с
intследите за внешними ключами, ростом вторичных индексов и запросамиORDER BY id— на v4 они перестают означать «по времени». - UUID не секрет: он утекает в логи, историю браузера и
Referer, поэтому не годится как токен доступа.



