Какие проблемы могут возникнуть при добавлении индексов на все поля
Что проверяют
Понимаете ли вы, что индекс ускоряет конкретные запросы, а платит за него каждая запись. Для Go-разработчика это обычно PostgreSQL за сервисом на pgx или database/sql. Следом спрашивают, как понять, какие индексы действительно нужны.
Как отвечать
Чем плохи индексы «на всё»:
- медленная запись: каждый
INSERTобновляет все индексы таблицы. Десять индексов это десять дополнительных структур, которые надо поправить на каждую строку; - потеря HOT-обновлений: PostgreSQL обновляет строку без правки индексов (HOT update), если не изменилась ни одна индексированная колонка и на странице есть место. Индекс на каждом поле лишает этой оптимизации почти любой
UPDATE: больше записи, больше WAL, быстрее растёт bloat; - память: индексы конкурируют с данными за
shared_buffersи кеш ОС. Рабочий набор перестаёт помещаться в память, и замедляется уже чтение; - WAL и реплики: больше журнала, дольше бэкапы, сильнее отставание реплик;
- обслуживание:
VACUUMпроходит по каждому индексу, аCREATE INDEXбезCONCURRENTLYв миграции блокирует запись в таблицу; - польза меньше ожидаемой: одиночные индексы плохо заменяют составной под
WHERE a = $1 AND b = $2 ORDER BY c. Планировщик может склеить их через BitmapAnd, но это дороже, а индекс по полю с низкой селективностью он просто игнорирует.
Правильный путь: индексы от запросов. Берёте тяжёлые запросы из pg_stat_statements, смотрите EXPLAIN (ANALYZE, BUFFERS), строите составные и частичные индексы под них. Неиспользуемые находятся в pg_stat_user_indexes по idx_scan = 0.
Где спотыкаются
Думают, что индексы стоят только места на диске. Главная цена в записи и потере HOT.
Расставляют gorm:"index" на каждое поле структуры, а AutoMigrate послушно создаёт всё это на проде.
Удаляют «ненужный» индекс по статистике одного узла. Счётчики idx_scan у мастера и реплик свои, и индекс может работать на реплике, куда ходит отчётный сервис. Уникальные индексы не трогают вовсе: они держат ограничение, а не скорость.