> Почему использовать UUID вместо int для идентификаторов (Go)
Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: Ozon
Стек: Go
> Пример ответа
Короткий ответ
UUID выбирают вместо int для глобальной уникальности без централизованной координации, что критично в распределённых системах, микросервисах и при миграциях/слияниях данных. Int удобен для внутренних ключей с автоинкрементом, но создаёт проблемы с предсказуемостью, коллизиями при шардировании и раскрытием объёма данных. UUID (особенно v7) даёт приемлемый компромисс между уникальностью, сортируемостью и производительностью индексов. Выбор зависит от контекста: OLTP с одним инстансом - int, распределённая система - UUID.
Подробное объяснение
Основные причины использования UUID:
- Глобальная уникальность без координации - генерация на клиенте или в любом сервисе не требует обращения к центральному счётчику, что устраняет bottleneck и упрощает офлайн-режимы.
- Безопасность и недетерминизм - int-идентификаторы предсказуемы (id=100500 → можно дёргать API), UUID v4 содержит 122 бита случайности, что затрудняет перебор.
- Миграции и слияния - при объединении баз данных или переносе записей между окружениями int-ключи конфликтуют, UUID - нет.
- Шардирование - распределение по шардам на основе int-ключа требует сложной логики (range, hash), UUID можно генерировать с учётом шарда (например, в v7 первые биты - timestamp, дальше - шард).
- Кэширование и интеграции - внешние системы (клиенты, партнёры) получают стабильный идентификатор, не зависящий от внутренней нумерации.
Минусы UUID:
- Размер - 16 байт против 4-8 у int, что увеличивает размер индексов и памяти.
- Случайность - v4 ломает кластеризацию в B-tree, вызывая page splits и фрагментацию индексов. Решается UUID v7 (time-ordered) или ULID.
- Читаемость - строковое представление (36 символов) неудобно в логах и отладке.
Сравнение с int:
| Критерий | int | UUID |
|---|---|---|
| Размер | 4-8 байт | 16 байт |
| Генерация | централизованная | локальная |
| Предсказуемость | да | нет (кроме v7) |
| Сортировка | естественная | v7 - да, v4 - нет |
| Индекс | компактный | тяжёлый |
В Go типичные подходы: uuid.New() из github.com/google/uuid (v4) или github.com/gofrs/uuid с поддержкой v7. Для PostgreSQL - тип uuid нативно, для MySQL - BINARY(16) или CHAR(36).
На практике
- Когда int оправдан: внутренние сущности с высокой частотой вставки (логи, события), где нет внешнего доступа и шардирования. Автоинкремент даёт компактность и предсказуемость порядка.
- Когда UUID обязателен: публичные API, мультитенантные системы, синхронизация между сервисами, event sourcing, CQRS.
- Гибридный подход: int как внутренний ключ (PK), UUID как внешний
public_idс уникальным индексом. Так делают, когда нужна скорость join'ов и безопасность внешних ссылок. - Производительность: для UUID v7 в PostgreSQL индекс работает почти как для bigint, но всё равно больше по размеру. Для v4 - ожидайте деградацию на больших таблицах (миллионы строк).
- Генерация на клиенте - плюс для офлайн-режимов, минус - нельзя доверять порядку без timestamp.
Пример кода
GOpackage mainimport ("fmt""github.com/google/uuid""time")// UUID v7 - time-ordered, сортируемыйfunc newUUIDv7() uuid.UUID {// в google/uuid нет v7, используем gofrs/uuid// пример для gofrs/uuid:// id, _ := uuid.NewV7()// return idreturn uuid.New() // v4 для демонстрации}type User struct {ID uuid.UUID `db:"id"`Name string `db:"name"`}func main() {id := uuid.New()fmt.Println(id.String()) // 6ba7b810-9dad-11d1-80b4-00c04fd430c8// для сортируемых идентификаторов в PostgreSQL:// CREATE EXTENSION IF NOT EXISTS pgcrypto;// CREATE TABLE users (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), ...)// для v7: CREATE TABLE users (id uuid PRIMARY KEY DEFAULT uuidv7(), ...)}
Как отвечать на собеседовании
Начните с главного trade-off: глобальная уникальность без координации против размера и производительности индексов. Затем уточните контекст - спросите, о какой системе речь (распределённая или монолит). Если спрашивают абстрактно, структурируйте ответ:
- Назовите ключевые причины: распределённость, безопасность, миграции.
- Признайте минусы: размер, фрагментация индексов для v4.
- Упомяните современные решения: UUID v7, ULID, Snowflake - они решают проблему сортировки.
- Дайте практическую рекомендацию: для публичных API - UUID, для внутренних высоконагруженных таблиц - int или bigint.
- Приведите пример из Go:
github.com/google/uuidилиgofrs/uuid, типuuidв PostgreSQL.
Не углубляйтесь в детали реализации, если не просят. Держите ответ в рамках 2-3 минут, затем предложите обсудить конкретный сценарий.
Что проверяет интервьюер
- Понимание trade-off между уникальностью, производительностью и сложностью.
- Знание особенностей индексов в БД (кластеризация, page splits).
- Осведомлённость о современных форматах (v7, ULID) - признак актуальности знаний.
- Умение адаптировать ответ под конкретный стек (Go + PostgreSQL/MySQL).
- Способность аргументировать выбор, а не просто заучивать "UUID - это хорошо".
Типичные ошибки
- Однобокость: "UUID всегда лучше" или "int всегда лучше" - нет, всё зависит от сценария.
- Игнорирование v7: упоминание только v4 без сортируемости выглядит устаревшим.
- Путаница с безопасностью: UUID не шифрует данные, он лишь недетерминирован.
- Забывают про размер индекса: на таблицах с миллиардами строк разница между 4 и 16 байтами существенна.
- Предлагают UUID для внутренних join'ов без причины - это увеличивает память и время запросов без пользы.
- Не упоминают гибридный подход (int + public_id) - это сильный ответ, показывающий практический опыт.
> Похожие задачи по Go
Как реализовать шардирование кэша
Как работает двусвязный список в LRU кэше
Напиши SQL запрос для выбора всех чатов пользователя с именем Вася
Как шардировать таблицы пользователей, чатов и сообщений
> Похожие задачи по backend
Как реализовать шардирование кэша
Как работает двусвязный список в LRU кэше
Напиши SQL запрос для выбора всех чатов пользователя с именем Вася
Как шардировать таблицы пользователей, чатов и сообщений
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью