Primary key vs natural key
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем это различать
На собеседовании по SQL или data modeling часто спрашивают: «какой primary key выбрать — auto-increment или email?». Это вопрос о surrogate (искусственном) vs natural (естественном) ключе. От выбора зависит performance, readability, и возможность рефакторинга бизнес-логики.
В классических учебниках говорят «используйте natural key, если существует». В реальности 95% баз — surrogate. Знать обе стороны — базовый навык аналитика / data engineer.
В статье:
- Короткий ответ и определения
- Плюсы и минусы каждого подхода
- Примеры из реальной жизни
- Composite keys как компромисс
- Best practices 2026
Короткий ответ
- Surrogate key (PK) — искусственный идентификатор (auto-increment INT, UUID). Не несёт бизнес-смысла.
- Natural key — значение из бизнес-домена (email, username, ИНН, VIN).
В большинстве современных систем — surrogate. Natural — для справочников и legacy.
Surrogate key
CREATE TABLE users (
id SERIAL PRIMARY KEY, -- surrogate
email VARCHAR(255) UNIQUE,
name VARCHAR(100)
);Плюсы
- Стабильность: не меняется, даже если email изменится
- Компактный: 4-8 байт (INT/BIGINT), лучше чем длинная строка
- Быстрые JOIN: integer comparison быстрее string
- Приватность: в URL / логах не светит email
- Независимость от бизнеса: бизнес-правила меняются, ID нет
Минусы
- Нет семантики
- Нужен дополнительный UNIQUE index на бизнес-ключ
- Легко случайно создать дубли (с разными ID, но тем же email)
Natural key
CREATE TABLE countries (
code CHAR(2) PRIMARY KEY, -- natural: 'RU', 'US'
name VARCHAR(100)
);Плюсы
- Читаемость: сразу понятно, что
RU— Россия - Не нужен дополнительный lookup
- Меньше таблиц (для справочников)
Минусы
- Мутабельность: бизнес может поменять код
- Длиннее: string vs int
- JOIN медленнее: string comparison
- Приватность: если это email / VIN — утекает в логи
Пример плохого natural key
Email как PK users:
CREATE TABLE users (
email VARCHAR(255) PRIMARY KEY, -- плохая идея
...
);
CREATE TABLE orders (
user_email VARCHAR(255) REFERENCES users(email), -- FK на email
...
);Проблема: пользователь меняет email — нужно обновить FK везде. CASCADE UPDATE теоретически работает, но на миллионах записей — боль.
С surrogate:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE -- меняется свободно
);
CREATE TABLE orders (
user_id INT REFERENCES users(id) -- стабильный FK
);Типичные surrogate keys
Auto-increment INT / BIGINT
id SERIAL PRIMARY KEY -- Postgres
id INT AUTO_INCREMENT PRIMARY KEY -- MySQLПлюсы: компактный, readable в URL (/user/42).
Минусы: гадкой в distributed системах (collision).
UUID
id UUID PRIMARY KEY DEFAULT gen_random_uuid()Плюсы: distributed-safe, не светит порядок created. Минусы: 16 байт, slow на индексе (random).
Snowflake / ULID
Временно-упорядоченные ID. Компромисс между INT и UUID.
Composite keys
Комбинация нескольких полей:
CREATE TABLE order_items (
order_id INT,
product_id INT,
qty INT,
PRIMARY KEY (order_id, product_id) -- composite
);Для junction-таблиц и history-таблиц. Естественно: один product в одном order_id встречается один раз.
Best practices
1. Default — surrogate (auto-increment INT или UUID)
Для 95% таблиц.
2. Natural key как UNIQUE constraint
Гарантирует уникальность email без использования его как PK:
email VARCHAR(255) UNIQUE NOT NULL3. Справочники — natural key ok
countries с PK code CHAR(2) — нормально. Значения стабильны.
4. Distributed системы — UUID
Если данные создаются в разных регионах / системах.
На собесе
«Surrogate или natural key?» Surrogate по default. Natural — для справочников с immutable кодами.
«Email PK — почему плохо?» Email меняется. Все FK ломаются или CASCADE UPDATE.
«UUID или INT?» INT default. UUID в distributed / privacy-sensitive.
«Что такое composite key?» PK из нескольких колонок. Для junction table.
Частые ошибки
1. Использовать mutable natural key
Email, phone, username — могут меняться. Плохой PK.
2. Использовать password как natural id
Никогда. В PK вообще не должно быть sensitive данных.
3. Не добавить UNIQUE на natural-like column
Если email не PK, но должен быть уникальным — UNIQUE обязателен.
4. Смешивать подходы в проекте
Одна таблица — natural, другая — surrogate. Консистентность лучше.
Связанные темы
FAQ
UUID замедляет?
На B-tree индексе — да (random inserts). ULID / sequential UUID v7 решают.
Auto-increment collision в sharding?
Да, поэтому в distributed — UUID или ID generator.
Можно PK изменить?
Технически — да. На практике — риск сломать FK и migrations. Почти не делают.
Surrogate всегда INT?
Исторически. Сейчас UUID популярны для microservices.
Тренируйте data modeling — откройте тренажёр с 1500+ вопросами для собесов.