Сотрудник поддержки открывает обращения крупного клиента. В небольшой тестовой базе список появляется мгновенно, в рабочей — приходится ждать. Команда предлагает увеличить сервер. До согласования расходов стоит посмотреть, как база вообще ищет эти обращения. Иногда она перебирает гораздо больше данных, чем нужно экрану.
Индекс — дополнительная структура для поиска. Он позволяет находить нужные записи по выбранным признакам, не просматривая всю таблицу. Но его полезность определяется запросами и распределением данных. Само наличие индекса ещё не означает, что поиск стал дешевле.
Один запрос объясняет больше, чем размер таблицы
Предположим, поддержке нужны последние открытые обращения конкретной организации. В запросе есть клиент, статус, сортировка по времени и ограничение числа результатов. Индекс только на статус может оказаться бесполезным: открытых обращений много, и среди них ещё предстоит найти нужного клиента.
Возможный вариант — составной индекс, где организация помогает сузить поиск, а следующие поля соответствуют фильтрации и сортировке. Но универсального рецепта «добавим все поля в индекс» нет. Важны порядок полей, тип индекса, варианты запросов и реальные объёмы каждой выборки. Особенности составных индексов описаны в документации PostgreSQL.
Для одного клиента история короткая, у другого накопились годы переписки. Один сотрудник ищет незакрытые обращения, другой выгружает все за квартал. Проверка только на удобном примере может показать красивое ускорение, которого остальные пользователи не получат.
Иногда задача решается частичным индексом: в него включают только записи, удовлетворяющие определённому условию. Например, незакрытые обращения, если именно они нужны часто и составляют небольшую часть истории. Такая структура может быть меньше полной, но помогает только подходящим запросам. Изменение статуса также должно корректно менять состав индекса.
План запроса — проверка гипотезы
У базы есть план выполнения: откуда читать строки, как их соединять, фильтровать и сортировать. Инженер может увидеть его через EXPLAIN и проверить, соответствует ли выбранный путь задаче. Руководителю не требуется читать каждую строку плана. Достаточно попросить объяснить, какая лишняя работа исчезнет после изменения.
Полезно сравнить оценку числа записей с фактическим результатом. Большое расхождение может указывать на устаревшую статистику или необычное распределение данных. Тогда причина плохого плана не обязательно в недостатке индексов. А если запрос возвращает большую часть таблицы, последовательное чтение иногда оказывается разумнее множества отдельных обращений через индекс.
Нужно различать EXPLAIN и EXPLAIN ANALYZE: второй вариант действительно выполняет запрос. Проверять таким способом тяжёлую операцию или изменение данных на рабочей системе без подготовки опасно. Руководство по планам выполнения полезно именно как инструмент диагностики, а не как повод экспериментировать на клиентских данных.
Бывает, что приложение для списка обращений отдельно запрашивает сведения о клиенте для каждой строки. Даже быстрый одиночный запрос тогда повторяется много раз. Индекс может сделать каждый вызов дешевле, но не устранит лишние сетевые обращения. Аналогично он не сократит передачу огромного ответа, если экрану нужны лишь несколько записей. Поэтому полезно посмотреть весь путь от действия сотрудника до готового экрана. Время внутри базы — только часть ожидания. Исправление способа загрузки данных иногда приносит больший эффект и не требует постоянно обслуживать ещё одну структуру.
Обслуживание индекса тоже занимает ресурсы
Дополнительную структуру нужно хранить и поддерживать при изменениях, которые её затрагивают. Она расходует место, участвует в работе с памятью и диском, может увеличивать объём служебных записей. Поэтому десяток индексов под редкие отчёты способен сделать массовую загрузку данных заметно тяжелее.
При этом неверно считать любое изменение строки одинаково дорогим для любого индекса. Детали зависят от базы, устройства индекса и изменяемых полей. Более того, индекс иногда ускоряет и сам поиск строк для UPDATE или DELETE. Итоговую цену операции надо измерять целиком, а не выводить из лозунга «чтение быстрее, запись медленнее».
В нашем примере полезно одновременно проверить открытие списка и создание нового обращения. Если поддержка выиграла секунды, а приём входящих сообщений начал отставать, это уже вопрос приоритетов продукта. Возможно, нужен другой индекс или отдельный путь для тяжёлого отчёта.
Создать индекс — отдельная рабочая операция
На большой таблице построение индекса может занять существенное время и конкурировать с клиентскими запросами за ресурсы. Обычный способ создания способен блокировать запись; доступность более щадящего режима зависит от СУБД. В PostgreSQL существует CREATE INDEX CONCURRENTLY, но это не бесплатное изменение: оно требует дополнительной работы и имеет свои ограничения.
План выпуска должен учитывать свободное место, текущую нагрузку, возможность отмены и проверку результата. Если построение завершилось неуспешно, нужно убедиться, в каком состоянии осталась структура. Сообщение в чате «индекс добавлен» не заменяет наблюдения за задержками и ошибками после релиза.
Особенно важно не объединять несколько независимых оптимизаций в один эксперимент. Когда одновременно меняют запрос, размер сервера и набор индексов, трудно понять, что помогло и что придётся вернуть назад при ухудшении.
Редко используемый не обязательно лишний
Статистика использования помогает находить кандидатов на удаление. Однако наблюдение за обычной рабочей неделей может не захватить закрытие месяца, сезонную нагрузку или расследование инцидента. Некоторые индексы обеспечивают уникальность данных. Их нельзя удалить только потому, что на графике чтений почти ничего нет.
У каждого кандидата стоит проверить назначение, связанные ограничения, длительность наблюдения и возможность восстановления. Список индексов полезно пересматривать вместе с изменениями продукта: закрытый экран и удалённый отчёт могут оставлять инфраструктуре работу, которая больше никому не нужна.
Завершённая оптимизация даёт понятный результат: ускоренный рабочий сценарий, измеренную цену записи и хранения, проверенный способ выпуска. Если изменение не улучшило нужную операцию, индекс не следует сохранять ради самого факта проделанной работы. Он будет продолжать занимать ресурсы и после того, как эксперимент забудут.