Как оптимизировать производительность SQL-запроса при правильных планах и индексах?
Что проверяют
Есть ли у вас второй эшелон приёмов, когда очевидное уже сделано. Вопрос отсеивает тех, у кого весь ответ это «добавить индекс».
Как отвечать
Если план хороший и индексы на месте, а всё равно медленно, причины обычно такие.
Слишком много данных на выходе. Запрос возвращает сто тысяч строк, а приложению нужны двадцать. Пагинация по ключу вместо OFFSET (тот всё равно читает и отбрасывает), выборка только нужных колонок, агрегация на стороне базы.
Слишком много запросов. Каждый быстрый, но их тысяча (N+1). Лечится пакетной выборкой, а не оптимизацией отдельного запроса.
Работа, которую можно не делать в реальном времени. Материализованное представление или заранее посчитанная витрина, обновляемая по расписанию. Отчёт, который считался тридцать секунд, начинает читаться за миллисекунды.
Конкуренция, а не сам запрос. Ожидание блокировок, длинные транзакции, исчерпанный пул соединений. Смотреть надо не план, а pg_stat_activity и время ожидания.
Настройки и ресурсы. Сортировка не помещается в work_mem и уходит на диск; устаревшая статистика даёт неверные оценки; раздутие таблицы после массовых обновлений требует уборки.
Архитектурные ходы. Кэш на стороне приложения, реплики для чтения, секционирование больших таблиц по дате, денормализация горячих счётчиков.
Где спотыкаются
Продолжают крутить индексы, когда проблема в количестве запросов или в блокировках.
Используют OFFSET на глубоких страницах.
Не различают «запрос медленный» и «запрос ждёт».
Не знают про материализованные представления и считают каждый отчёт заново.