Каковы недостатки использования натуральных ключей в реляционных базах данных?

«Каковы недостатки использования натуральных ключей в реляционных базах данных?» — вопрос из категории Базы данных, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Натуральный (естественный) ключ — это атрибут или набор атрибутов, имеющих бизнес-значение и уникально идентифицирующих запись (например, email, паспортные данные, артикул). Его использование сопряжено с серьезными проблемами:

Основные недостатки:

  1. Изменчивость (Mutability): Бизнес-данные могут меняться. Смена email пользователя потребует каскадного обновления всех внешних ключей в связанных таблицах, что сложно и рискованно.
  2. Низкая производительность: Строковые ключи (VARCHAR) обычно длиннее и сравниваются медленнее, чем целочисленные (BIGINT, INT). Это влияет на скорость JOIN, индексацию и размер индексов.
  3. Сложность обеспечения уникальности: В распределенных системах гарантировать глобальную уникальность значения (например, номера заказа) сложнее, чем использовать автоинкремент базы данных или UUID.
  4. Семантическая перегрузка: Ключ несет бизнес-смысл, который может со временем измениться или оказаться неверным.
  5. Сложность составных ключей: Если ключ состоит из нескольких полей, его использование в качестве внешнего ключа становится громоздким и неэффективным.

Рекомендуемый подход: Суррогатный ключ + уникальные ограничения.

-- НЕ РЕКОМЕНДУЕТСЯ: натуральный ключ
CREATE TABLE users (
    email VARCHAR(255) PRIMARY KEY, -- Проблема: если email изменится
    name VARCHAR(100)
);

-- РЕКОМЕНДУЕТСЯ: суррогатный ключ
CREATE TABLE users (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, -- Быстрый, неизменяемый
    email VARCHAR(255) NOT NULL UNIQUE, -- Бизнес-уникальность через ограничение
    name VARCHAR(100),
    -- Дополнительный индекс для частых поисков по email
    INDEX idx_email (email) 
);

-- Внешние ключи ссылаются на стабильный `id`
CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    user_id BIGINT NOT NULL, -- Ссылка на суррогатный ключ
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

Реализация в JPA/Hibernate:

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // Суррогатный ключ
    private Long id;

    @Column(unique = true, nullable = false) // Бизнес-уникальность
    private String email;

    // ... остальные поля
}

Исключения: Натуральные ключи иногда оправданы в справочных таблицах с небольшим, неизменным набором значений (например, country_code CHAR(2) PRIMARY KEY).