Какие недостатки у кластеризованного индекса в базах данных и когда его стоит избегать?

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

Ответ

Кластеризованный индекс (обычно PRIMARY KEY) определяет физический порядок данных в таблице. Его не всегда стоит использовать в качестве основного ключа по следующим причинам:

  1. Частые вставки в неконце таблицы: При вставке записи с ключом, попадающим в середину таблицы, происходит фрагментация страниц (page splits), что дорого с точки зрения производительности и места.
  2. Большой размер ключа: Ключ кластеризованного индекса включается во все некластеризованные (вторичные) индексы как указатель на строку. Большой ключ увеличивает размер всех вторичных индексов.
  3. Неэффективный доступ по другим полям: Если основные запросы фильтруют или соединяют таблицы по другим столбцам (например, user_id, created_at), кластеризация по первичному ключу может быть неоптимальной.

Пример проблемного сценария — UUID как первичный ключ:

-- Случайные UUID приводят к фрагментации при вставках
CREATE TABLE orders (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- Кластеризованный индекс
    user_id INT NOT NULL,
    amount DECIMAL
);
-- Запросы по user_id будут неэффективны без отдельного индекса.

Альтернативное решение: Использовать суррогатный, последовательно возрастающий ключ для кластеризации и создать отдельный уникальный индекс для бизнес-ключа.

CREATE TABLE orders (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, -- Узкий, возрастающий ключ
    external_id UUID UNIQUE NOT NULL, -- Бизнес-идентификатор
    user_id INT NOT NULL,
    amount DECIMAL,
    INDEX idx_user_id (user_id) -- Эффективный индекс для частых запросов
);