Поиск и удаление неиспользуемых и дублирующих индексов в PostgreSQL
- Медленная вставка и обновление данных (
INSERT/UPDATE) в сильно индексированных таблицах. - Чрезмерный объем кэша
shared_buffers, занятый «холодными» индексами. - Раздувание объема резервных копий из-за наличия идентичных или перекрывающихся индексов.
1. Поиск абсолютно неиспользуемых индексов (idx_scan = 0)
Запрос исключает первичные ключи и уникальные ограничения:
SELECT
schemaname || '.' || relname AS table_name,
indexrelname AS index_name,
pg_size_pretty(pg_relation_size(i.indexrelid)) AS index_size,
idx_scan AS index_scans
FROM pg_stat_user_indexes ui
JOIN pg_index i ON ui.indexrelid = i.indexrelid
WHERE NOT i.indisunique
AND NOT i.indisprimary
AND ui.idx_scan = 0
ORDER BY pg_relation_size(i.indexrelid) DESC
LIMIT 20;2. Поиск дублирующих и избыточных индексов
Например, индекс на (a) избыточен, если уже существует композитный индекс на (a, b):
SELECT
indrelid::regclass AS table_name,
indexrelid::regclass AS redundant_index,
indkey::text AS redundant_keys
FROM pg_index
WHERE indisvalid
ORDER BY indrelid;3. Безопасное удаление неиспользуемого индекса
Используйте CONCURRENTLY для исключения блокировки таблицы во время удаления:
DROP INDEX CONCURRENTLY IF EXISTS idx_orders_old_status;4. Сброс счетчиков статистики индексов
Если вы хотите начать чистый аудит после релиза:
SELECT pg_stat_reset(); Частые вопросы (FAQ)
Почему нельзя удалять индекс сразу, если idx_scan = 0?
Статистика pg_stat_user_indexes накапливается с момента последнего сброса или рестарта. Индекс может использоваться редко, но быть критически важным для ежемесячных тяжелых отчетов или регламентных задач.
Учитывает ли idx_scan сканирования по внешним ключам (Foreign Keys)?
Да, если планировщик использует этот индекс для проверки ограничений каскадного удаления (ON DELETE CASCADE) или соединений, это увеличивает счетчик idx_scan.
Блокирует ли DROP INDEX CONCURRENTLY запись в таблицу?
Нет. Команда ожидает завершения активных транзакций, удерживающих слабую блокировку, и удаляет метаданные без взятия исключительного AccessExclusiveLock на саму таблицу.
Как оценить суммарный размер всех индексов конкретной таблицы?
Выполните запрос: SELECT pg_size_pretty(pg_indexes_size('my_table_name'));.