Вопрос о количестве индексов на таблице часто становится предметом жарких обсуждений среди разработчиков и администраторов баз данных. Одни считают: чем больше индексов — тем лучше производительность. Другие предупреждают: избыточная индексация может замедлить запись и увеличить нагрузку.
Так где же золотая середина?
Далее предлагаем вашему вниманию перевод оригинальной статьи “How Many Indexes Is Too Many?”, который подготовила специалист «Автомакона». В статье рассматривается этот вопрос с практической стороны и даются полезные рекомендации.
Начнем с простого эксперимента.
Возьмём базу данных Stack Overflow (любого размера), удалим все индексы из таблицы Users и попытаемся удалить одну строку. Это поможет нам понять, насколько важно наличие нужных индексов и как сильно они влияют на производительность.
Выполним следующую команду, предварительно включив статистику ввода-вывода:
|
1 2 3 4 |
SET STATISTICS IO ON; GO BEGIN TRAN DELETE dbo.Users WHERE DisplayName = N’Brent Ozar’; |
Удаление происходит внутри транзакции, чтобы мы могли в любой момент откатить изменения и повторить операцию.

Этот запрос удаляет пользователя с именем ‘Brent Ozar’. Поскольку у нас нет индекса на столбец DisplayName, SQL Server вынужден просканировать всю таблицу, что приводит к значительным затратам ресурсов.
В частности, в копии базы данных Stack Overflow за июнь 2018 года SQL Server читает 143 670 страниц по 8 КБ каждая, чтобы найти нужную строку.

Нам нужен индекс по DisplayName.
Если мы хотим, чтобы операция удаления выполнялась быстрее, нам нужно быстро находить строки, где DisplayName = 'Brent Ozar'. Для этого отменим предыдущее удаление и создадим индекс:
|
1 2 |
ROLLBACK CREATE INDEX DisplayName ON dbo.Users(DisplayName); |
Теперь повторим операцию удаления:
|
1 2 |
BEGIN TRAN DELETE dbo.Users WHERE DisplayName = N’Brent Ozar’; |
Теперь, фактический план выполнения выглядит проще:

SQL Server начинает с индекса по DisplayName, моментально находит нужную строку, а затем удаляет её уже из кластерного индекса. Хотя визуально план может казаться простым, если навести курсор на оператор Clustered Index Delete, можно заметить, что фактически участвуют два объекта — PK_Users_Id (кластерный индекс) и DisplayName (некластерный индекс).

Если заглянуть в нижнюю часть всплывающей подсказки над оператором удаления, можно заметить, что SQL Server работает сразу с двумя объектами: это кластерный индекс PK_Users_Id и наш новый некластерный индекс DisplayName. Несмотря на это, объём работы теперь существенно меньше — и это хорошо видно по времени выполнения запроса и количеству логических чтений:

Объём работы значительно снижается: теперь вместо 143 670 логических чтений — всего 12. Даже если вы не аналитик — результат явно впечатляющий.
Отлично! Индекс помог. Может, добавим ещё?
Когда ко мне приходят с проблемами производительности, чаще всего всё сводится к одной из двух крайностей: либо люди вообще не знают, как работают хорошие некластерные индексы, либо увлеклись ими настолько, что налепили их везде, где только можно. Давайте добавим ещё несколько и снова попробуем удалить строку:
|
1 2 3 4 |
CREATE INDEX Location ON dbo.Users(Location); CREATE INDEX LastAccessDate ON dbo.Users(LastAccessDate); CREATE INDEX Reputation ON dbo.Users(Reputation); CREATE INDEX WebsiteUrl ON dbo.Users(WebsiteUrl); |
На первый взгляд, план выполнения не изменился, но это обман.

Графический план скрывает важную деталь, и чтобы её увидеть, нужно навести курсор на оператор «Clustered Index Delete»:

Обрати внимание на список объектов внизу всплывающего окна — несмотря на название «Clustered Index Delete», на самом деле SQL Server удаляет строку не только из кластерного индекса, но и из каждого некластерного. Это значит, что для выполнения операции требуется больше логических чтений — ведь нужно пройтись по всем индексам, где эта строка тоже присутствует:

Количество чтений выросло с 12 до 24. Звучит пугающе? На самом деле нет. Несколько дополнительных индексов — это нормально, особенно если они действительно нужны для ваших запросов.
Однако, чем больше индексов мы добавляем…
Давайте добавим еще несколько индексов, каждый из которых содержит несколько включенных столбцов:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
CREATE INDEX Age ON dbo.Users(Age) INCLUDE (DisplayName, Location, Reputation);
CREATE INDEX CreationDate ON dbo.Users(CreationDate) INCLUDE (DisplayName, Location, Reputation);
CREATE INDEX DownVotes ON dbo.Users(DownVotes) INCLUDE (DisplayName, Location, Reputation);
CREATE INDEX UpVotes ON dbo.Users(UpVotes) INCLUDE (DisplayName, Location, Reputation);
CREATE INDEX EmailHash ON dbo.Users(EmailHash) INCLUDE (DisplayName, Location, Reputation); |
И снова выполняем удаление. План остался прежним…

Но логических чтений стало ещё больше:

Это всё ещё не катастрофа — и в большинстве случаев ваша нагрузка и оборудование вполне справятся с 5, 10, а может, даже 15 или 20 индексами. Главное помнить, что это всё равно намного лучше, чем те 143 670 логических чтений, с которых мы начинали!
Так сколько индексов — это слишком много?
Нет «магического» числа индексов. Всё зависит от таких факторов, как:
-
Рабочая нагрузка в основном на чтение
-
Нет проблем с транзакциями или блокировками
-
Очень быстрые железо и запросы
-
Пользователи не так чувствительны к скорости вставок/обновлений/удалений
Вместо того чтобы искать конкретное число индексов, обратите внимание на признаки, что у вас их слишком много для вашей нагрузки и оборудования:
-
У ваших коллег возникают жалобы на замедление операций вставки, обновления или удаления данных.
-
Программы мониторинга сигнализируют о проблемах взаимоблокировки, либо подобные инциденты замечают сами пользователи.
-
Уже пробовали настроить уровни изоляции транзакций (RCSI или SI), но проблема осталась нерешённой.
При появлении перечисленных признаков настало время прибегнуть к бесплатному инструменту с открытым исходным кодом sp_BlitzIndex. Запустив его без аргументов прямо в нужной базе данных, вы получите чёткий отчёт о возможных проблемных местах:
-
Избыточных индексах-двойниках или близнецах.
-
Полностью неиспользуемых индексах, которые никак не задействуются системой.
-
Индексах с огромным объёмом данных, но редкими обращениями к ним.
Оптимизация индексов является ключевым аспектом эффективной работы любой базы данных. Как показало наше исследование, правильное использование индексов способно значительно повысить производительность приложения, сокращая время обработки запросов и снижая нагрузку на систему.
Однако важно помнить, что создание большого количества индексов также имеет свою цену: оно увеличивает накладные расходы на обслуживание, замедляет операции вставки и обновления данных, а также повышает вероятность возникновения конфликтов блокировки.
Следовательно, баланс между производительностью и поддерживаемостью — это задача, требующая тщательного анализа рабочих нагрузок, структуры данных и особенностей используемого оборудования. Регулярное тестирование и мониторинг позволяют выявить излишне созданные или неэффективные индексы, а инструменты вроде sp_BlitzIndex помогают упростить этот процесс.
Помните: правильный подбор индексов — залог стабильной и быстрой работы вашей базы данных.
ссылка на оригинал статьи https://habr.com/ru/articles/907600/
Добавить комментарий