Кейс: как я снял нагрузку с сервера и базы, просто добавив индексы в PostgreSQL
Есть баги, которые честно говорят, что они баги: “500 Internal Server Error”, “syntax error at or near”. А есть баги-лицемеры.
Открываю бэкофис, жму привычную кнопку — и в ответ:
Request Error (Error: Network Error)
Ну конечно. Сеть. Хотя интернет работает, Wi-Fi не мигает, кот не перегрыз кабель. Значит, это не сеть. Это — классика жанра: запрос в базе уехал в отпуск и не вернулся вовремя.
Шаг 1. Воспроизведение (или “поймай меня, если сможешь”)
Я делаю то же самое, что делал пользователь: тот же экран, тот же фильтр/кнопка. Ошибка повторяется.
Отлично: баг не стеснительный, ловится руками.
Шаг 2. Логи
Дальше по чеклисту:
- лезу в backend-логи,
- нахожу endpoint,
- вытаскиваю реальный SQL (с параметрами),
- иду в базу и гоняю его на сервере, а не “у меня в консольке на деве”.
Делаю замер:
EXPLAIN (ANALYZE, BUFFERS)-- тот самый запрос;
Потому что “кажется, медленно” — это эмоция. А EXPLAIN ANALYZE — это диагноз.
Шаг 3. План выполнения: где съедаются секунды
На плане видно главное: запрос не “просто выбирает 50 строк”. Он:
- фильтрует по условиям,
- выполняет подзапросы/SubPlan,
- дергает функции,
- и в какой-то момент вынужден читать лишнее (а потом выкидывать).
Шаг 4. Я не переписывал запрос. Я дал базе “указатель”
Дальше я сделал то, что в PostgreSQL почти всегда дает самый быстрый эффект:
- посмотрел, где именно уходит время (самые дорогие узлы плана);
- проверил, по каким колонкам фильтруем/джойним/сортируем;
- посмотрел структуры таблиц, в которые упираются функции/подпланы;
- добавил точечные индексы под реальные условия.
Важно: я не “накидал индексов на всё подряд”. Я добавил их ровно туда, где план показывал боль.
Финал. Минус таймауты, плюс спокойствие
После индексов:
- запрос стал отвечать заметно быстрее (с ~27с до ~12с, потом до ~4с),
- сервер перестал в пике страдать от лишнего чтения,
- а на фронте “Network Error” внезапно исцелился, потому что запрос перестал выбивать таймаут.
И да, это тот редкий случай, когда “добавил индексы” — не отмазка, а реально решение.
Что такое индекс
Представь бумажную книгу на 1000 страниц. И тебе нужно найти слово “PostgreSQL”.
Без алфавитного указателя (без индекса)
Ты делаешь “Seq Scan по книге”:
- открываешь страницу 1,
- читаешь,
- не нашёл — страница 2,
- …
- страница 837 — нашёл.
Это линейный способ: чем больше книга, тем больнее.
С алфавитным указателем (с индексом)
Ты открываешь конец книги: “Указатель”. Там по алфавиту:
- PostgreSQL → 837, 842, 901
И ты сразу идёшь на нужные страницы.