Ниже — несколько проблем, которые выглядят как проблемы производительности БД, но на деле ими не являются.
Пользователи бесконечно жмут refresh
Когда пользователю кажется, что страница не загружается, он по привычке нажимает reload. Каждый reload повторно запускает те же самые запросы. Мы часто видели это на тяжёлых отчётах. Когда нагрузка была низкой, отчёты генерировались мгновенно, но с ростом числа клиентов, объёма данных и сложности логики запросы стали выполняться дольше, плюс выросло время форматирования на уровне приложения. Если кнопка “run report” никак не показывает, что процесс уже идёт, пользователи начинают нажимать её повторно или обновлять страницу.
Признаки:
• резкий рост
calls в pg_stat_statements без соответствующего роста трафика• всплеск запросов, привязанный к загрузке конкретной страницы
• пользователи жалуются, что страница «сломалась» или «грузится вечность»
Решение: состояния загрузки с отключением кнопок, optimistic UI, асинхронная генерация отчётов или хотя бы обычный spinner, который показывает, что приложение работает. Ни одна база не выдержит фронтенд, позволяющий пользователям DDOS’ить её вручную.
N+1 запросы из ORM
Вы загружаете список из 100 заказов. ORM затем выполняет ещё 100 отдельных запросов, чтобы получить клиента для каждого заказа. Потом дашборд показывает общее число заказов для каждого клиента — и это ещё 100 запросов. В итоге вместо одного запроса страница делает 201.
Каждый запрос занимает 2 мс. Но 201 × 2 мс = 404 мс последовательных round-trip’ов к БД на каждый запрос страницы. При 500 запросах в минуту одна страница генерирует тысячи лишних запросов в минуту, которых вообще не должно существовать.
Решение: свести всё к одному запросу через
JOIN и агрегации. Некоторые ORM умеют это автоматически, в других придётся писать SQL вручную.Агрессивный polling чаще, чем меняются данные
Разработчик добавляет
setInterval(() => fetchStatus(), 1000) для проверки статуса заказа. Код уезжает в production. 50 000 активных сессий, опрашивающих сервер раз в секунду = 50 000 запросов в секунду к таблице, которая обновляется всего раз в минуту.Решение: WebSocket, Server-Sent Events или хотя бы существенно увеличить интервал polling’а.
Запрос медленный только для нового сценария использования
Отчётный запрос выполняется 800 мс. Финансовый отдел запускает его один раз в день в 9 утра. Потом кто-то добавляет этот запрос в widget на дашборде с автообновлением каждые 30 секунд. Теперь каждый пользователь с открытым дашбордом запускает запрос на 800 мс по 120 раз в час.
Сам запрос медленнее не стал. Изменился паттерн доступа.
Решение: кешировать отчёт через materialized view, вынести его в background job или убрать auto-refresh.
Иногда проблема — в дизайне системы, а не в БД
Не раз бывало так, что запись экрана пользователя с воспроизведением ошибки помогала понять проблему быстрее, чем любые логи.
👉 @SQLPortal