Кладбище забытых индексов
История о поиске неиспользуемых индексов через pg_stat_user_indexes и аккуратном освобождении дискового места в PostgreSQL.
КейсPostgreSQLИндексыХранение— Ну что, Вася, три недели прошло. Диски вашей базе уже добавили? — Макс усмехнулся, откидываясь на спинку кресла.
Вася горько вздохнул:
— Ха-ха. Прикинь, до какого маразма довели: наша заявка уже вторую неделю находится на согласовании у инфобеза. Видимо оценивают, как добавление дисков к базе отчётности привлечёт внимание хакерских группировок.
Макс покачал головой:
— А свободное место на диске как было, так и осталось на грани?
— Да, и постоянно уменьшается, — Вася выдохнул. — Но я уже не удивляюсь. Помню, как в прошлом году месяц пытался получить права на просмотр логов. До сих пор помню эти бессмысленные переписки и анкеты. А потом вдруг у какого-то начальника не сформировался отчёт, подняли шум, и доступ предоставили за 15 минут.
Макс усмехнулся:
— А ты до сих пор это помнишь. Ну, тогда не удивляйся, что у твоей базы тоже столько мёртвых индексов. Она ведь тоже не забывает, что когда-то их создали.
— Мёртвых? — Вася нахмурился. — Что ты имеешь в виду?
— Когда я изучал, что у вас там занимает место, заметил, что у многих таблиц по 10 и больше индексов. Я понимаю, что отчётность, аналитика и всё такое, но это выглядит сильно избыточным. Напомни, у вас статистику использования объектов давно сбрасывали?
— Точно не помню… Кажется, пару месяцев назад, после последнего перезапуска базы.
— А мы можем посмотреть, — вспомнил Макс и развернул ноутбук.
SELECT datname, stats_resetFROM pg_stat_database;— Действительно два с половиной месяца, этого уже вполне достаточно. Смотри сюда.
Макс открыл скрипт.
SELECT s.schemaname, s.relname AS table_name, s.indexrelname AS index_name, s.idx_scan AS index_scans, pg_size_pretty(pg_relation_size(s.indexrelid)) AS index_size, pg_get_indexdef(s.indexrelid) AS index_definition, i.indisvalid, i.indisprimaryFROM pg_stat_user_indexes s JOIN pg_index i ON i.indexrelid = s.indexrelidWHERE s.idx_scan = 0ORDER BY pg_relation_size(s.indexrelid) DESC;— Этот запрос покажет все индексы, по которым не было index scan с момента последнего сброса накопительной статистики. Только не спеши их удалять.
— Это почему ещё? — Вася выглядел так, будто только что осознал, что у него в квартире поселился ненужный сосед.
— Ну всё же надо посмотреть на каждый индекс внимательно. Индекс может быть нужен для PRIMARY KEY, UNIQUE и так далее. Лучше подготовить список для удаления и разослать коллегам на согласование. Чтобы не бегали потом с криками «Кто удалил? Прибью когда найду!»
Вася кивнул:
— Спасибо! Теперь у меня есть новая цель.
Они вместе задумчиво посмотрели на результаты запроса. Макс усмехнулся:
— Ну что, Вася, видишь, сколько мёртвого груза в базе накопилось?
— Угу, — кивнул Вася. — Кто бы мог подумать, что забытые и никому не нужные индексы занимают столько места.
Макс отставил кружку на стол и потянулся:
— Да, всё как в жизни. Мы с возрастом тоже так накапливаем. Вроде бы тащим с собой какие-то обиды, надежды, представления о том, как должно быть. Кажется, что это важно, но на деле — только место занимает.
Он постучал по экрану:
— Поэтому иногда полезно пересмотреть то, что давно кажется «устоявшимся». Избавиться от того, что на самом деле уже мешает. Базу — от ненужных индексов. А себя — от старых фантазий и обид.