Что делать, если индекс есть, но база решила не использовать его?
Что проверяют
Понимаете ли вы, что планировщик стоимостной, и что «не использует индекс» чаще всего означает «посчитал полное сканирование дешевле». Дальше смотрят, умеете ли вы проверить, прав ли он.
Как отвечать
Основной инструмент это EXPLAIN (ANALYZE, BUFFERS). Сравнивать надо не только время, а ожидаемое число строк с фактическим. Расхождение в разы означает, что планировщик ошибся не в логике, а в оценке, и чинить надо статистику: ANALYZE, более детальная гистограмма, расширенная статистика для связанных колонок.
Когда полное сканирование действительно выгоднее:
- таблица маленькая или выборка забирает заметную долю строк. Индекс окупается на выборке малой части;
- стоимость случайного чтения в настройках осталась как для дисков со шпинделем, и на быстрых дисках индексный доступ выглядит дороже, чем есть.
Когда индекс просто не подходит:
- тип параметра не совпадает с типом колонки. Из кода на Go это частая история: колонка
bigint, а в запрос уезжает другой числовой тип или строка; - частичный индекс, условие которого не покрывает запрос;
- сортировка в
ORDER BY ... LIMITне совпадает с порядком в индексе; - индекс остался невалидным после неудачного построения без блокировки: он существует и виден, но не используется;
- индекс раздут, и обход по нему стоит дороже расчётного.
Отдельная деталь для Go: драйвер по умолчанию готовит выражения, и на перекошенных данных база может перейти на общий план вместо плана под конкретные параметры. Лечится режимом выполнения без подготовки или настройкой кэша планов.
Где спотыкаются
Отключают полное сканирование настройкой сессии и объявляют задачу решённой. Это способ проверить гипотезу, а не рабочая конфигурация: в проде такой переключатель прячет проблему и ломает другие запросы.
Проверяют на тестовой базе с тысячей строк, где сканирование законно дешевле, и переносят вывод на прод. Сравнивать планы имеет смысл на сопоставимых объёмах и распределении данных.
Чинят запрос, не глядя на оценку строк. Если планировщик ошибся на порядок, переписывание условий ничего не даст, пока не обновлена статистика.