Какие плюсы и минусы у создания индексов по низкоселективным столбцам?

Ответ

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

Плюсы (когда индекс может быть полезен):

  • Ускорение запросов с высокочастотными фильтрами: Если часто выполняются запросы с WHERE status = 'active', индекс может ускорить выборку, даже если он отфильтрует только 10% строк.
  • Улучшение производительности для составных индексов: Низкоселективный столбец может быть полезной первой частью составного (композитного) индекса. Например, индекс (status, created_date) эффективен для запроса WHERE status = 'pending' ORDER BY created_date.
  • Покрывающие индексы (Covering Index): Если индекс включает все поля, запрашиваемые в SELECT, то запрос может быть выполнен полностью по индексу, избегая обращения к основной таблице (heap или clustered index).

Минусы:

  • Низкая отдача при сканировании: Оптимизатор запросов часто предпочтет полное сканирование таблицы (Table Scan) использованию такого индекса, если он отфильтрует слишком большую часть данных (например, >20-30%). Это связано с дополнительными затратами на чтение самого индекса.
  • Затраты на обслуживание: Индекс занимает место на диске и в памяти. Каждая операция INSERT, UPDATE или DELETE в индексируемом столбце требует обновления индекса, что снижает производительность записи.
  • Ложное чувство оптимизации: Разработчик может ошибочно полагать, что создание индекса решит проблему производительности, в то время как реальная проблема — в неоптимальной структуре запроса или отсутствии более селективного индекса.

Практический пример (SQL):

-- Возможно, бесполезный низкоселективный индекс:
CREATE INDEX IX_Users_IsActive ON Users(IsActive); -- Значения: 0 или 1

-- Гораздо более полезный составной индекс, где IsActive — ведущая часть:
CREATE INDEX IX_Users_IsActive_LastName ON Users(IsActive, LastName);

-- Запрос, который может использовать составной индекс эффективно:
SELECT Id, LastName FROM Users 
WHERE IsActive = 1 AND LastName LIKE 'Smith%';

Рекомендация: Перед созданием индекса по низкоселективному столбцу проанализируйте планы выполнения запросов. Часто лучшим решением является создание составного индекса, где этот столбец сочетается с более селективными.

Ответ 18+ 🔞

А, слушай, про низкоселективные индексы — это вообще отдельная песня, блядь. Ну, типа, индекс по колонке, где значений — раз-два и обчёлся. Типа пол, статус или вот эта ебучая галочка удалён. Создавать такой — это как играть в русскую рулетку, только с производительностью базы, ёпта.

С одной стороны, иногда он таки полезен, вот реально:

  • Если по нему постоянно долбят запросы. Ну, например, каждый второй запрос — WHERE status = 'active'. Даже если активных всего 10%, а оптимизатор по индексу их выцепит — уже хорошо, блядь. Быстрее, чем всю таблицу шаманить.
  • Как стартовая часть в составном индексе — вообще огонь. Вот смотри: сам по себе status — говно. Но если сделать индекс (status, created_date), то запрос WHERE status = 'pending' ORDER BY created_date будет летать, как угорелый. Потому что данные уже отсортированы, как надо.
  • Если индекс покрывающий (covering). О, это магия, блядь! Если в самом индексе уже лежат все поля из SELECT, то база может вообще не лезть в основную таблицу. Всё решится на уровне индекса — красота, ядрёна вошь!

Но, сука, минусов тоже овердохуища:

  • Оптимизатор его может просто послать нахуй. Серьёзно. Если по индексу отсеется меньше, скажем, 20-30% строк, то ему проще всю таблицу целиком просканировать. Зачем ему читать ещё и индекс, если итого всё равно почти всё выбирать? Затраты на логику, блядь.
  • Он же не бесплатный, пидор! Место жрёт, в памяти виснет. И главное — каждая операция вставки, обновления или удаления по этой колонке теперь должна этот индекс тоже обновить. Так что если у тебя таблица активно пишется — готовься к тормозам на записи, ебать.
  • Создаст ложное чувство безопасности, блядь. Разработчик поставит индекс, хлопнет себя по ляжке: "Всё, оптимизировал!" А на деле запрос как тормозил, так и тормозит, потому что проблема была в другом месте. Или нужен был не этот индекс, а другой.

Короче, смотри на практике, как оно бывает:

-- Вот это вот, скорее всего, бесполезный индекс. Ну, че он даст по колонке с 0 и 1?
CREATE INDEX IX_Users_IsActive ON Users(IsActive);

-- А вот это уже дело! Составной индекс, где IsActive — только первый кирпичик.
CREATE INDEX IX_Users_IsActive_LastName ON Users(IsActive, LastName);

-- И запрос, который этот индекс выжмет до конца:
SELECT Id, LastName FROM Users 
WHERE IsActive = 1 AND LastName LIKE 'Smith%';

Так что моя рекомендация, чувак: Не кидайся создавать такие индексы сходу. Включи мозги, посмотри планы выполнения (EXPLAIN ANALYZE или что там у тебя). Чаще всего выход — сделать составной индекс, где этот низкоселективный столбец идёт первым, а за ним уже что-то более конкретное. Тогда да, будет толк. А так — только место зря занимать.