Сколько кластерных индексов может быть у таблицы в реляционной СУБД?

Ответ

Только один кластерный индекс на таблицу. Это фундаментальное ограничение в большинстве реляционных СУБД (SQL Server, MySQL/InnoDB, PostgreSQL).

Почему только один? Кластерный индекс определяет физический порядок хранения данных на диске. Данные в таблице сами являются «листьями» (leaf level) кластерного индекса. Поскольку строки не могут быть физически упорядочены одновременно двумя разными способами, кластерный индекс может быть только один.

Альтернатива: некластеризованные индексы. Для ускорения других запросов можно создавать множество некластеризованных индексов. Они представляют собой отдельные структуры (например, B-деревья), которые хранят ключи индекса и указатели на соответствующие строки данных (в виде ключа кластерного индекса).

Пример в T-SQL (SQL Server):

-- Создание таблицы с кластерным индексом по полю Id (чаще всего PRIMARY KEY).
CREATE TABLE dbo.Orders (
    Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED, -- Единственный кластерный индекс
    OrderDate DATETIME NOT NULL,
    CustomerId INT NOT NULL,
    TotalAmount DECIMAL(10,2) NOT NULL
);

-- Создание некластеризованных индексов для ускорения поиска по другим полям.
-- Их может быть много (в пределах технических ограничений СУБД).
CREATE NONCLUSTERED INDEX IX_Orders_CustomerId ON dbo.Orders(CustomerId);
CREATE NONCLUSTERED INDEX IX_Orders_OrderDate ON dbo.Orders(OrderDate DESC);

Практические рекомендации по выбору поля для кластерного индекса:

  • PRIMARY KEY — частый и хороший кандидат, особенно если это монотонно возрастающий ключ (IDENTITY, последовательность), что минимизирует фрагментацию.
  • Выбирайте столбец (или столбцы), по которым часто происходят запросы диапазонов (range scans) (например, WHERE Date BETWEEN ...).
  • Избегайте выбора широких (много столбцов) или часто изменяемых (volatile) столбцов, так как это влияет на производительность всех некластеризованных индексов и может вызывать фрагментацию.

Ответ 18+ 🔞

А, ну это классика, блядь! Сидишь такой, думаешь: "А сделаю-ка я два кластерных индекса на одну таблицу, будет быстрее!" А нихуя! Это как пытаться одновременно запихнуть данные в шкаф, отсортированные и по цвету рубашек, и по размеру. Физически, ёпта, невозможно! Данные на диске — они в одном экземпляре лежат, как последняя бутылка водки на троих.

Представь: таблица — это вот эта самая куча данных, листики в дереве. Так вот, кластерный индекс — это и есть само это дерево, ствол и ветки, а данные — это листья на нём. И дерево-то оно одно, блядь! Не может одна папка в твоём компе быть одновременно и в "Документах", и в "Загрузках" в виде оригинальных байтов. Поэтому кластерный индекс — единственный и неповторимый, как первая любовь, только с техническими ограничениями.

А как же другие запросы ускорить, спросишь? Да легко! Для этого есть некластеризованные индексы. Их — овердохуища можно наделать, в разумных пределах, конечно. Это как оглавление в конце учебника или алфавитный указатель. Отдельная структура, где написано "Иванов — см. стр. 45", а сама страница 45 — это уже кластерный индекс, то есть сами данные.

Смотри на примере, чтобы вообще пиздец как понятно стало:

-- Вот создаём таблицу заказов. PRIMARY KEY чаще всего и есть наш главный кластерный мужик.
CREATE TABLE dbo.Orders (
    Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED, -- Всё! Единственный кластерный! Больше нихуя!
    OrderDate DATETIME NOT NULL,
    CustomerId INT NOT NULL,
    TotalAmount DECIMAL(10,2) NOT NULL
);

-- А это наши палочки-выручалочки — некластеризованные индексы. Хошь по клиенту, хошь по дате — нате, получайте!
CREATE NONCLUSTERED INDEX IX_Orders_CustomerId ON dbo.Orders(CustomerId);
CREATE NONCLUSTERED INDEX IX_Orders_OrderDate ON dbo.Orders(OrderDate DESC);

Теперь, главный вопрос: а на что этот единственный кластерный индекс вешать-то, чтобы не обосраться?

  • Чаще всего — на PRIMARY KEY. Особенно если это IDENTITY или sequence — данные просто подписываются в конец, как в очередь, фрагментации минимум. Идеально.
  • Вешай на то, по чему часто ищешь диапазоны. Ну, WHERE дата BETWEEN '2023' AND 'сейчас'. Если данные физически в порядке дат лежат — СУБД просто кусок прочитает и всё, быстро.
  • Не вешай на широкие или часто меняющиеся поля. Представь: у тебя кластерный ключ — это "ФИО + Дата рождения". Каждый раз, как кто-то фамилию меняет после замужества, всю таблицу двигать надо! А все остальные индексы, которые ссылаются на этот ключ, тоже придётся обновлять. Пиздец, а не производительность. И фрагментация будет, как после хорошей пьянки.

Короче, запомни: один кластерный — священная корова. Всё остальное — некластеризованные костыли, но очень быстрые и полезные. Держи баланс, и будет тебе счастье.