Проблемы повторяются из проекта в проект. 10 самых частых — с оценкой эффекта.
1. Отсутствие индексов на внешние ключи
orders.customer_id без индекса. Любая выборка «заказы клиента X» — полный перебор. На 100 000 заказов это 200 мс вместо 2. Исправление: CREATE INDEX ON orders(customer_id). Эффект: ×50–100.
2. N+1 запросы
ORM делает N+1 запросов вместо одного с JOIN. Типично в NestJS + TypeORM или Prisma. Решение: include / with / relations либо явный JOIN. Эффект: ×10–50 на списках.
3. Индексы на низкоселективных полях
Индекс на is_active (boolean) или status (5 значений) бесполезен — планировщик всё равно выберет последовательное сканирование. Плюс замедляет INSERT/UPDATE. Решение: удалить, при необходимости — частичный индекс (WHERE is_active = true).
4. Слишком много индексов
15 индексов на таблицу с 20 полями. Каждый UPDATE обновляет все. На таблице заказов критично. Решение: аудит pg_stat_user_indexes, удаление неиспользуемых (idx_scan = 0).
5. Отсутствие партиционирования на больших таблицах
Таблица журналов на 50 млн строк. DELETE старых записей — часы, VACUUM не справляется. Решение: партиционирование по дате (RANGE), автоматическое удаление старых партиций. DELETE превращается в DETACH PARTITION за миллисекунды.
6. Неправильные типы данных
VARCHAR(255) вместо TEXT, CHAR(1) для статуса, JSON вместо нормализованных полей. Миграция через ADD COLUMN + UPDATE + DROP.
7. VACUUM и autovacuum
По умолчанию autovacuum слишком ленивый. На активных таблицах раздутие достигает 50%+. Запросы замедляются в разы. Решение: autovacuum_vacuum_scale_factor для активных таблиц (0.05 вместо 0.2). Периодический pg_repack.
8. Ненастроенный shared_buffers
По умолчанию 128 MB. На сервере с 32 GB RAM — катастрофа, кэш не работает. Решение: shared_buffers = 25% RAM, effective_cache_size = 50–75% RAM. Эффект: ×2–5 на запросах, ограниченных диском.
9. Слишком много соединений
PostgreSQL плохо работает с 500+ соединениями. Каждое — процесс, переключение контекста съедает CPU. Решение: PgBouncer в режиме пула транзакций + max_connections до 100. Эффект: ×3–5.
10. Отсутствие мониторинга
Без pg_stat_statements не видно, какие запросы на самом деле медленные. Все оптимизации — наугад. Решение: включить расширение, панель в Grafana, оповещение при 95-м процентиле больше 100 мс.
С чего начинать
- Включить
pg_stat_statements. - Найти топ-10 по
total_time. - Посмотреть
EXPLAIN ANALYZE. - Добавить недостающие индексы.
- Исправить N+1.
- Настроить параметры сервера.
- Настроить autovacuum.
- Партиционирование.
- PgBouncer.
- Мониторинг.
Выводы
PostgreSQL умеет работать быстро, но не «из коробки». 80% проблем — индексы, N+1 и параметры сервера. Прежде чем добавлять кэш или горизонтальное масштабирование — пройдитесь по чек-листу.