Полгода назад наш основной эндпоинт /products отдавал ответ за 800 мс на p95. Это разбор того, что мы сделали, чтобы довести цифру до 80 мс.

Симптомы

Мониторинг показывал деградацию не постепенно, а скачками — как только в таблицу заказов попадало больше 200k строк, время ответа каталога подскакивало вдвое.

Диагностика

  • Включили pg_stat_statements и нашли топ по total_time

  • Обнаружили классический N+1: категория и вендор подгружались отдельным запросом на каждый продукт

  • Отсутствовал составной индекс на (delivery_type, is_active, created_at)

Что исправили

Заменили последовательные Preload на GORM на явные JOIN'ы через VIEW product_catalog, добавили частичные индексы под самые частые фильтры каталога и вынесли подсчёт total_downloads в денормализованное поле с фоновым пересчётом.

Индекс — это не бесплатно: каждый лишний индекс замедляет запись. Мы удалили три неиспользуемых индекса, прежде чем добавлять новые.

Результат

p95 упал с 800 до 80 мс, нагрузка на CPU базы снизилась почти вдвое. Полный список миграций — в репозитории проекта.