Проверка индексов базы данных после релиза: что проверить на второй неделе
Проверка индексов базы данных через две недели после релиза помогает найти медленные запросы, убрать лишнее и исправить пути запросов, которые staging не показал.

Почему неделя запуска скрывает медленные запросы
Неделя запуска редко показывает правду о производительности базы данных. Трафик неравномерный, новая функция ещё не вошла в привычное ежедневное использование, и многие паттерны запросов ещё не проявились. Проверка индексов работает лучше после короткой паузы, когда реальное поведение заменяет догадки.
Staging — одна из причин. Тестовые базы обычно меньше, аккуратнее и намного менее запущены, чем продакшн. В них меньше старых записей, меньше NULL-ов, меньше крайних случаев и меньше истории, привязанной к каждой учётной записи. Запрос, который кажется мгновенным на 50 000 аккуратных строк, может тормозить, когда попадает на миллионы смешанных записей.
Реальные пользователи — другая причина. Они не идут по тому ровному пути, который ваша команда показывала на демо. Они возвращаются, обновляют страницу, открывают несколько вкладок, сортируют список тремя способами и накладывают фильтры, которые никто не думал комбинировать. Это меняет SQL, который отправляет приложение. Иногда та же таблица вдруг начинает получать WHERE или ORDER BY, на которые раньше не обращали внимания.
Новые фильтры и сортировки — частая ловушка. Один лишний фильтр, одна новая колонка для сортировки или одно поле поиска могут создать новый путь запроса. Если старый индекс соответствовал старому шаблону, он может почти ничего не дать для нового. Тогда база просматривает гораздо больше строк, чем ожидалось.
Фоновые задания усугубляют ситуацию, потому что они конкурируют за те же таблицы. Релиз часто добавляет не только пользовательские экраны, но и задачи синхронизации, отчёты, уведомления, обновления кэша или аналитические задания. В staging эти задачи могут запускаться редко или на крошечных наборах данных. В продакшне они работают рядом с пользовательским трафиком и превращают лёгкое замедление в заметную проблему.
Достаточно небольшого примера. Представьте новую страницу заказов, где можно фильтровать по статусу и сортировать по updated_at. В день релиза ею пользуются единицы, и всё кажется нормальным. Через две недели служба поддержки использует её весь день, финансы экспортируют данные из той же таблицы, а фоновая задача обновляет статусы заказов каждые несколько минут. Таблица получает больше чтений, больше записей и паттерн запросов, на который исходные индексы не были рассчитаны.
Именно поэтому медленные запросы часто проявляются после релиза, а не во время него. Код не стал внезапно хуже — реальное использование просто показало то, чего тестовая среда не могла показать.
Что проверять через две недели
Две недели дают то, чего нет в staging: обычный трафик. К этому моменту начинают накапливаться странные фильтры, более широкие диапазоны дат и повторяющиеся фоновые задачи. Это подходящее время, чтобы проверить индексы по обычной нагрузке, а не по пику дня релиза.
Начните с логов медленных запросов за обычные будние дни. Выбирайте дни, которые отражают реальное использование продукта. Вторник и среда обычно дают более правдивую картину, чем день релиза, выходные или утро после большого анонса.
Затем сопоставьте эти медленные запросы с релизом. Посмотрите страницы, которые чаще всего открывают пользователи, задачи, которые теперь запускаются по расписанию, и любые отчёты или выгрузки, которые добавила функция. Новые пути запросов часто прячутся в рутинных действиях — например, открытие списка с одним лишним фильтром, а не в том экране, который все демонстрировали.
Сортируйте по влиянию, а не по неожиданности
Не заостряйте внимание только на одном самом медленном запросе. Это ловит драматичные провалы, но пропускает тихие проблемы, которые ежедневно тратят часы времени базы. Сортируйте запросы по количеству вызовов, по суммарному времени и по p95, затем сравнивайте результаты.
Каждый взгляд показывает разные проблемы. Запрос, который выполняется 200 000 раз по 40 мс, может навредить больше, чем один отчёт, который занимает 8 секунд. Плохой p95 часто указывает на отсутствие поддержки для распространённого фильтра или сортировки.
Пути чтения по большим таблицам обычно требуют внимания в первую очередь. С ними легче безопасно работать, чем с путями, интенсивно модифицирующими данные, и они затрагивают больше пользователей. Если релиз добавил фильтрацию по заказам, invoice-ам, событиям или аудит-логам, проверьте эти запросы в первую очередь. Большие таблицы быстро наказывают за полные сканирования.
Здесь появляется знакомый паттерн. Функция хорошо работает в тестировании, но реальные пользователи комбинируют фильтры так, как никто не пробовал. Они открывают список клиентов, фильтруют по статусу, сортируют по последней активности и перелистывают страницы одну за другой. Один экран может превратиться в тысячи повторяющихся чтений по большой таблице.
Если времени мало, начните с четырёх вещей: топ медленных запросов по будням, наиболее используемые страницы релиза, запланированные задачи, которые сканируют большие таблицы, и отчёты с широкими диапазонами дат. Этот короткий проход обычно находит пробелы, которые стоит закрыть до того, как пользователи начнут жаловаться.
Как проводить проверку
Две недели после релиза у вас наконец появляется реальное поведение для анализа. Пользователи кликают в странных порядках, фильтры комбинируются по-новому, и одна небольшая функция может отправлять в таблицу гораздо больше чтений, чем кто-либо ожидал.
Начните с самого релиза, а не с базы данных. Откройте changelog, board спринта или заметки к релизу и выпишите каждое изменение, которое могло затронуть данные. Новые поля поиска, опции сортировки, статус-фильтры, дашборды, выгрузки, фоновые задачи и админские экраны — всё это считается.
Затем работайте в фиксированном порядке:
- Сопоставьте каждую доставленную фичу с запросами, которые она триггерит.
- Получите планы из продакшена для самых загруженных запросов через логи, статистику базы или APM.
- Сравните ожидаемый план с тем, который выбрала база.
- Проверьте, изменился ли шаблон запроса после релиза.
- Меняйте по одному элементу и снова измеряйте.
Этот порядок важен. Если прыгнуть сразу к добавлению индексов, можно исправить не тот запрос или создать перекрытие, которое замедлит записи. Одно аккуратное изменение говорит больше, чем три быстрых предположения.
Держите область проверки узкой. Сначала проверяйте самые загруженные новые пути, затем те, которые после релиза стали медленнее. Если фича едва используется, оставьте её в покое, если только она явно не вызывает боль.
Команды с lean-подходом часто делают это короткой еженедельной привычкой, а не экстренным исправлением. Такой подход соответствует идее AI-augmented operations, о которой говорит Oleg Sotnikov: наблюдать реальное поведение, вносить взвешенные изменения и избегать глобальных переделок, когда достаточно небольшого исправления.
Когда проверка закончена, сохраните планы до и после с короткой заметкой о том, что и почему изменили. Эта запись ускорит следующий пострелизный проход и поможет не допустить повторения той же ошибки.
Простой пример из реального релиза
Новая страница заказов может выглядеть безобидно в staging. При 5 000 строк фильтры кажутся мгновенными, сортировка дешёвой, и никто не видит проблем.
Через две недели продакшн может рассказать совсем другую историю. В этом примере та же страница работала на базе примерно из 8 миллионов заказов, и пользователи действовали быстро. Они фильтровали по статусу, выбирали диапазон дат, сортировали по новизне и переключались между командами.
Форма трафика важнее дизайна страницы. Страница не запускала один гигантский отчёт. Она выполняла один и тот же небольшой запрос снова и снова, обычно чтобы получить последние заказы для одной команды.
Запрос выглядел примерно так:
SELECT id, status, owner_id, created_at
FROM orders
WHERE team_id = $1
AND status = $2
AND created_at >= $3
AND created_at < $4
ORDER BY created_at DESC
LIMIT 50;
В базе уже были отдельные индексы на status и created_at. Это звучит разумно, но не соответствовало реальному использованию страницы. Базе всё равно приходилось просеивать слишком много строк и сортировать их, и эта стоимость проявлялась каждый раз, когда кто-то переключался между видами по командам.
Исправление — один композитный индекс, который соответствовал самому загруженному пути:
CREATE INDEX idx_orders_team_status_created_at
ON orders (team_id, status, created_at DESC);
После этого база могла сразу переходить к самым свежим совпадающим строкам для конкретной команды и статуса внутри окна дат. Ожидание, которое держалось в районе 1–2 секунд, упало до заметно менее 100 миллисекунд на том пути, которым люди пользовались чаще всего.
Фильтр по владельцу всё ещё существовал, но это не было первым местом, которое стоило индексировать. Реальный трафик показал, что переключение команд, фильтрация по статусу и сортировка «сначала новые» встречались гораздо чаще, чем поиск по владельцу. Именно это staging пропустил.
Вот почему проверка индексов работает лучше после начала реального использования. Вы не угадываете, какой фильтр может иметь значение позже. Вы смотрите на точные пути запросов, по которым люди ходят весь день, и добавляете индекс под этот путь вместо индексирования каждой колонки на экране.
Признаки того, что индекс поможет
Обычно для обнаружения проблемы с индексом не нужна полная переработка запроса. Вторая неделя часто выдаёт её сама. Реальные пользователи повторяют одни и те же действия, и эти паттерны нагружают одни и те же колонки снова и снова.
Сильный признак — повторение в WHERE. Если приложение всё день запрашивает заказы по customer_id, задачи по status или события по диапазону created_at, база постоянно идёт по одному и тому же пути. Начните отсюда: повторяющиеся фильтры часто превращаются в повторяющиеся сканирования таблицы.
Сортировка — ещё один индикатор. Если запрос сначала фильтрует строки, а затем сортирует их, база может при этом делать много работы. Вы почувствуете это в фидах, админских таблицах и экранах отчётов, где пользователи открывают один и тот же вид несколько раз в час. Хорошо поставленный индекс может поддерживать и фильтр, и сортировку.
Соединения тоже требуют внимания, особенно по большим колонкам внешних ключей. Фича может выглядеть нормально в staging на небольшой выборке, но тормозить в продакшне, когда один join касается миллионов строк. Если новый экран соединяет комментарии с постами, счета с клиентами или логи с пользователями, проверьте, индексирована ли колонка для join правильно.
Форма трафика имеет значение. Если после релиза чтения выросли, а записи остались примерно на том же уровне, добавление индекса часто выглядит оправданным. Но всё равно тестируйте, потому что индексы добавляют стоимость записи. На путях, насыщенных чтениями, такое решение часто имеет смысл.
Отчёт, который тратит гораздо больше времени, чем предполагает число строк — ещё один классический симптом. Если дашборд возвращает 200 строк, но занимает 4 секунды, редко проблема в размере результата. Стоимость обычно в том, как база находит, соединяет и сортирует данные до того, как вернёт эти 200 строк.
Проверьте эти шаблоны в первую очередь:
- Один и тот же фильтр появляется в логах медленных запросов много раз в день.
- Запрос тратит время на сортировку после фильтрации.
- Join использует большой столбец внешнего ключа.
- Нагрузка на чтение выросла, а нагрузка на запись — нет.
- Маленький отчёт занимает значительно больше времени, чем предполагает размер результата.
Если два или три из этих признаков встречаются вместе, протестируйте индекс, прежде чем переписывать приложение.
Ошибки, которые делают всё хуже
Проверка индексов сбивается с пути, когда команда реагирует на каждый медленный запрос добавлением ещё одного индекса. Это кажется безопасным, но часто превращает маленькую проблему в большую. Лишние индексы занимают место, замедляют записи и усложняют будущую настройку.
Одна распространённая ошибка — создавать индекс для отчёта, который запускается раз в месяц. Если отчёт сканирует много данных, но никто не ждёт его результаты в рабочее время, индекс может не окупиться. Более важными обычно являются загруженные пути вроде процесса оформления заказа, синхронизации сообщений или обновлений заказов.
Ещё одна западня — добавление перекрывающихся индексов, потому что каждый запрос выглядит немного иначе на бумаге. Допустим, у вас уже есть индекс (account_id, created_at, status), а кто-то через неделю добавляет (account_id, created_at). Во многих случаях более длинный индекс уже покрывает короткий путь. Теперь вы платите за запись дважды почти без выигрыша.
Где команды обычно ошибаются
Худшие последствия часто случаются на таблицах, которые меняются весь день. Каждая вставка, обновление или удаление должна поддерживать каждый индекс на таблице. Добавьте два или три лишних индекса к загруженной таблице событий или заказов — и задержки записи могут быстро вырасти. Чтения станут быстрее на 20 миллисекунд, а записи — медленнее по всему приложению. Это плохой обмен.
Staging тоже вводит в заблуждение. Тестовые данные меньше, аккуратнее и менее скошены, чем продакшн. Планы запросов, которые кажутся нормальными в staging, могут развалиться, когда реальные пользователи начнут применять странные фильтры, длинные диапазоны дат или неравномерные размеры арендаторов. Если продакшн говорит, что запрос стал медленным после релиза, доверьтесь продакшну.
Небольшой пример иллюстрирует это наглядно. Команда запускает новую панель клиента. Внутренний аналитический отчёт идёт медленно, и они добавляют три индекса за один день. Отчёт чуть ускорился, но записи заказов начали отставать, потому что та же таблица обрабатывала живую активность. Они также не могли понять, какой индекс помог, потому что изменили всё сразу.
Вот почему дисциплина помогает:
- Меняйте по одному индексу, когда это возможно.
- Измеряйте результат до и после.
- Ведите заметки о времени запроса, количестве строк и влиянии на записи.
- Удаляйте дублирующие или неиспользуемые индексы после теста.
Если вы измените пять индексов до того, как измерите один результат, след теряется. Команда начинает догадываться, и индексный мусор растёт тихо, пока следующий релиз не даст о себе знать.
Быстрые проверки перед отправкой изменения
Новый индекс может исправить реальное замедление, но также может добавить тормоза в другом месте. Любой индекс занимает место, заставляет INSERT и UPDATE делать больше работы и может изменить план, который база выбирает для соседних запросов.
Начните с использования, а не теории. Если запрос взлетел в день релиза из-за того, что одна фоновая задача запускалась слишком часто, возможно, вам не нужно продолжать его оптимизировать. Посмотрите на последние несколько дней трафика и проверьте, сохраняется ли нагрузка на этот путь в нормальном потоке.
Затем сравните план до и после изменения. Не останавливайтесь на «запрос стал быстрее на моём ноутбуке». Запустите EXPLAIN или EXPLAIN ANALYZE на том же виде запроса, с реалистичными фильтрами и порядком сортировки. Проверьте, используется ли новый индекс, сколько строк читается и избегается ли сортировка или большой скан. Если план почти не изменился, индекс вряд ли стоит держать.
Несколько проверок ловят большинство плохих ставок:
- Измеряйте p95-латентность, а не только среднее время.
- Наблюдайте CPU в пиковые периоды, а не в тихом тесте.
- Проверьте ожидания блокировок, если запрос трогает горячие строки.
- Оцените размер индекса перед добавлением на большую таблицу.
- Подумайте о стоимости записи, если таблица часто получает INSERT или UPDATE.
Эта стоимость записи важнее, чем команды ожидают. На таблице, ориентированной на чтения, ещё один индекс может быть дешёв. На таблице заказов или событий, которая меняется весь день, тот же индекс может замедлить записи настолько, что это повлияет на всё приложение. В lean-настрое лишняя нагрузка на CPU от плохого индекса проявляется быстро.
Держите очистку отдельно от срочного исправления. Если новый индекс решает живую проблему, внедрите его, наблюдайте за метриками несколько дней и запланируйте отдельный проход по удалению мёртвых или дублирующих индексов. Смешивание очистки с исправлением в день релиза усложняет проверку и повышает риск удалить то, что ещё нужно.
Коротко: подтвердите, что запрос всё ещё важен, сравните реальные планы, проверьте p95, CPU и ожидания блокировок и оцените стоимость записи перед слиянием.
Что делать дальше
Относитесь к этой проверке как к части релиза, а не как к уборке, которую делают только после жалоб. Реальный трафик меняет пути запросов, и эти изменения часто проявляются через неделю–две, когда больше пользователей затрагивает фичу по-разному.
Простая привычка работает: внесите проверку на дни 10–14 в календарь релиза. Это даст вашей команде достаточно реального использования, чтобы найти медленные запросы рано, пока ещё есть время их исправить до того, как накопятся обращения в поддержку.
Держите процесс лёгким. Добавьте задачу на follow-up при деплое фичи, сохраните несколько реальных примеров запросов с их планами выполнения в том же тикете, спросите продукт и поддержку, где пользователи чувствуют задержки, и примите одно небольшое решение после проверки: добавить индекс, переписать запрос или оставить как есть.
Сохранение примеров запросов важнее, чем многие команды думают. Тикет «поиск стал медленным» трудно обработать. Тикет с запросом, планом и одной заметкой о том, когда он тормозит, даёт команде конкретику, с которой можно работать дальше.
Продукт и поддержка обычно слышат о проблеме первыми. Пользователи редко говорят: «этот запрос нужно индексировать». Они говорят, что страница подвисает после смены фильтра или отчёт долго открывается в 9 утра. Эти детали часто указывают прямо на места, которые staging пропустил.
Если вы уже делаете такие проверки, держите заметки рядом с записью релиза. С течением времени появляются закономерности. Может быть, каждая фича с экспортом требует второго прохода. Может быть, сортировка по недавно добавленному полю даёт проблемы. Эти паттерны помогают больше, чем разовые исправления.
Если одни и те же проблемы продолжают возвращаться, внешняя помощь может ускорить процесс. Oleg Sotnikov на oleg.is работает со стартапами и малыми командами как Fractional CTO, помогая им проверять пути запросов, сокращать инфраструктурные потери и выстраивать лучшие релиз-привычки.
Следующий релиз уже должен иметь запланированную эту проверку до деплоя. Когда это станет нормой, пострелизная индексация перестанет быть борьбой с пожарами и превратится в регулярное обслуживание.
Часто задаваемые вопросы
Почему стоит подождать примерно две недели перед проверкой индексов?
Подождите, пока обычный будний трафик не заменит шум запуска. К 2-й неделе пользователи комбинируют фильтры, сортировки, выгрузки и повторные открытия страниц так, как staging никогда не показывал, поэтому вы сможете настроить именно те запросы, которые действительно имеют значение.
С чего начинать проверку после релиза?
Смотрите медленные запросы в будние дни, затем свяжите их со страницами, задачами и отчётами, которые добавил релиз. Сначала фокусируйтесь на больших таблицах и загруженных путях чтения — они обычно задевают больше пользователей.
Достаточно ли медленных логов запросов, чтобы найти проблемы с индексами?
Нет. Логи показывают, какие запросы медленные, но вам нужен план выполнения в продакшне, чтобы понять, почему база выбрала именно этот путь. Часто проблему решает не железо и не переписывание запроса, а другой, более подходящий индекс.
Как понять, что фильтр нуждается в индексе?
Ищите фильтры, которые повторяются в WHERE весь день, особенно на больших таблицах. Если один и тот же фильтр встречается часто и база просматривает много строк, обычно стоит протестировать индекс.
Стоит ли индексировать каждую новую колонку для сортировки?
Не всегда. Если пользователи сортируют небольшой набор результатов редко, база справится сама. Индекс стоит добавлять, когда фильтр и сорт часто идут вместе и база тратит заметное время на сортировку после поиска совпадений.
Когда композитный индекс лучше, чем отдельные индексы?
Композитный индекс имеет смысл, когда пользователи часто выполняют ту же комбинацию фильтров и сортировки. Отдельные индексы по одному столбцу редко подходят запросу вида team_id + status + created_at DESC так же хорошо, как один индекс, заточенный под этот путь.
Могут ли фоновые задачи сделать запросы медленнее после релиза?
Да, очень часто. Синхронизации, выгрузки, отчёты и обновления статусов используют те же таблицы, что и пользователи, и могут превратить слабую задержку в заметную проблему, когда нагрузка в продакшне растёт.
Какие запросы стоит исправлять в первую очередь?
Сначала ставьте в приоритет загруженные чтения на больших таблицах: списки, ленты, админки и отчёты, которые открывают весь день. Один запрос, который выполняется тысячи раз по 40 мс, часто дороже редкого отчёта, который занимает несколько секунд.
Как безопасно протестировать новый индекс?
Тестируйте одно изменение за раз на реалистичном виде запроса, затем сравните EXPLAIN или EXPLAIN ANALYZE до и после. Наблюдайте p95-латентность, CPU, количество прочитанных строк и влияние на записи в течение нескольких дней, чтобы убедиться, что индекс оправдывает свою стоимость.
Какие ошибки усугубляют настройку индексов?
Не добавляйте индекс на каждый медленный запрос и не накапливайте перекрывающиеся индексы на загруженных таблицах. Лишние индексы занимают место, замедляют вставки и обновления и усложняют дальнейшую настройку. Ведите заметки и удаляйте дубликаты после проверки.