PostgreSQL стал bottleneck
Классический сценарий деградации выглядит так: под нагрузкой растёт CPU, latency у ключевых сценариев уезжает вверх, очередь начинает отставать, а приложение всё чаще ждёт базу. Первая реакция почти всегда одинаковая — срочно добавить индексы. Но именно здесь чаще всего и начинается лишняя работа без результата.
Почему индексы “не помогают”
Индекс сам по себе не лечит плохой execution plan. Если запрос устроен неудачно, условие слабо селективно, join раздувает промежуточные наборы данных, а планировщик выбирает дорогой путь, новый индекс может вообще не использоваться или давать минимальный эффект.
Более того, иногда индексы ухудшают ситуацию: увеличивают стоимость записи, раздувают таблицы и создают ложное ощущение, что база уже “оптимизирована”, хотя корень проблемы остаётся в форме запроса или схеме доступа к данным.
Типовые ошибки
- добавлять индексы без EXPLAIN ANALYZE
- смотреть на запрос из кода, но не на реальный план выполнения
- игнорировать фактическое число строк, которые проходит план
- не проверять сортировки, hash aggregate, nested loop и seq scan в контексте объёма данных
- оптимизировать один запрос, хотя bottleneck создаёт целый набор похожих запросов
Что нужно делать вместо этого
- сначала собрать список реально дорогих запросов
- смотреть EXPLAIN ANALYZE, а не предполагать причину на глаз
- сравнивать estimated rows и actual rows
- проверять, какой участок плана даёт основную стоимость
- решать, нужен ли новый индекс, переписывание условия, изменение join-порядка или batch-подход
Что часто оказывается реальной причиной
На практике bottleneck нередко создают не “плохие индексы”, а комбинации: большой OFFSET, неудачная пагинация, фильтрация по вычисляемым значениям, тяжёлые join по широким таблицам, повтор одного и того же запроса много раз за один request, агрегации по горячим данным без предварительной подготовки.
Практический вывод
Когда PostgreSQL становится bottleneck, добавление индексов без анализа — это почти всегда стрельба вслепую. Сначала нужен реальный execution plan, потом локализация узкого участка, и только после этого — точечное изменение в запросе, индексе или способе чтения данных. Именно такой порядок даёт предсказуемый результат, а не имитацию оптимизации.
Когда нужен paid diagnostic
Если у команды уже есть список тяжёлых запросов и проблема воспроизводится в конкретных сценариях, обычно можно заходить сразу через Performance Triage. Если же “БД грузит CPU”, но никто не понимает, какой именно запрос или путь чтения данных реально создаёт bottleneck, безопаснее и быстрее начать с paid diagnostic, чтобы не стрелять индексами вслепую.