
Что такое 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, случайный. Все значащие биты, кроме служебных, заполняются случайными числами. Со временем создания и устройством такой UUID никак не связан, это просто уникальное значение. Самый распространённый вариант по умолчанию, если нет причины выбрать другой.
- v7, временная метка плюс случайность. Стандартизирован в 2024 году и быстро набирает популярность. Начинается с Unix-времени создания в миллисекундах, дальше идут случайные биты. Поэтому UUID v7, сгенерированные один за другим, сортируются в том же порядке, в котором были созданы. Для первичного ключа в базе данных это удобно: новые записи физически ложатся в конец индекса, а не в случайное место, как с v4.
- v1, временная метка плюс идентификатор узла. Старый стандарт. Он тоже содержит время создания, но вместо случайных битов в него зашит идентификатор устройства, изначально MAC-адрес сетевой карты. Из-за этой утечки информации об источнике v1 сейчас используют редко, v7 даёт похожую сортируемость без такого недостатка.
- NIL UUID, особый случай, 32 нуля подряд
(
00000000-0000-0000-0000-000000000000). Он не идентифицирует ничего и работает условной «пустой» заглушкой, когда поле обязано быть UUID, а значения ещё нет.
Какую версию выбрать для базы данных
Если UUID стоит первичным ключом таблицы, вариант по умолчанию v4 создаёт
проблему. Случайные значения ложатся в случайные места B-tree-индекса, и это
медленнее последовательной вставки. Версия 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, поэтому не годится как токен доступа.



