Как БД влияет на скорость приложения
Роль БД
Что происходит:
- Каждый запрос — SQL.
- Тяжёлый SELECT блокирует.
- Много запросов — сеть.
- Много соединений — CPU БД.
Что даёт:
- 70–90% времени запроса — БД.
- БД — главный bottleneck.
- Оптимизация БД — самый большой win.
Правило: хочешь ускорить приложение — начни с БД.
Что тормозит БД
Запросы:
- N+1.
- Без индексов.
- Тяжёлые JOIN.
- Полные сканы.
- *SELECT .
Схема:
- Отсутствие индексов.
- Неверные типы.
- Денормализация без причины.
- Нет партиционирования.
Инфра:
- Мало RAM.
- Медленный диск.
- Много соединений.
- Один сервер.
Правило: запросы, потом схема, потом инфра.
Метрики
Что смотреть:
- Query time — p95.
- Rows scanned — сколько просмотрено.
- Rows returned — сколько нужно.
- Index usage — используются ли.
- Cache hit — попадания.
- Connections — активные.
- Locks — блокировки.
Правило: если rows scanned >> rows returned — индекс нужен.
Роль индексов
Что дают:
- Быстрый поиск.
- Быстрая сортировка.
- Ускорение JOIN.
- Ускорение агрегатов.
Что стоят:
- Место на диске.
- Замедление INSERT/UPDATE.
- Сложность поддержки.
Правило: индекс — баланс между чтением и записью.
Как читать EXPLAIN
PostgreSQL:
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'a@b.com';
Что видно:
- Seq Scan — плохо на большой таблице.
- Index Scan — хорошо.
- Bitmap Heap Scan — хорошо.
- Nested Loop — для малых.
- Hash Join — для больших.
Правило: Seq Scan на 1M строк — проблема.
Rows estimate
Проблема: планировщик ошибается.
Пример:
rows=1 (actual=100000)
Что делать:
- ANALYZE таблицы.
- Увеличить statistics.
- Проверить индексы.
Правило: неверная оценка — плохой план.
Соединения
Проблема: много новых соединений.
Что даёт:
- CPU на setup.
- Память на каждый.
- Медленный TLS.
Решение: connection pooling.
Правило: pool — обязателен на высоких RPS.
Блокировки
Что бывает:
- Row lock — ждут.
- Table lock — стоп.
- Deadlock — откат.
Как найти:
SELECT * FROM pg_stat_activity WHERE wait_event_type = 'Lock';
Правило: длинные транзакции — источник блокировок.
Cache hit
Что важно:
- Shared buffers — кэш БД.
- Hit rate > 99% — хорошо.
- < 95% — мало RAM.
Как посмотреть:
SELECT sum(blks_hit) / sum(blks_hit + blks_read) AS hit_rate
FROM pg_stat_database;
Правило: hit rate < 99% — увеличить shared_buffers.
Slow queries
Что смотреть:
- Лог БД.
- pg_stat_statements.
- Pulse / Telescope.
Правило: топ-10 медленных — план оптимизации.
pg_stat_statements
Установка:
CREATE EXTENSION pg_stat_statements;
postgresql.conf:
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.max = 10000
pg_stat_statements.track = all
Запрос:
SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;
Что даёт: топ по суммарному времени.
Правило: pg_stat_statements — обязателен.
MySQL: performance_schema
SELECT * FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC LIMIT 20;
Что даёт: аналогично pg_stat_statements.
Правило: включить в my.cnf.
Подход
Порядок:
- Измерить — p95, slow queries.
- Найти топ-10.
- EXPLAIN каждый.
- Индексы / переписать.
- Измерить снова.
Правило: не угадывать — EXPLAIN.
Что важно в 2026
- PostgreSQL 16+.
- pgBouncer — pooling.
- Реплики — read scale.
- Партиционирование — для больших.
- pgvector — для AI.
- Citus — для шардирования.
Правило: PostgreSQL — дефолт для новых проектов.
С чего начать
Чек-лист:
- Включить pg_stat_statements.
- Собрать топ-20 slow queries.
- EXPLAIN каждую.
- Добавить индексы где нет.
- Проверить hit rate.
- Настроить shared_buffers.
- Установить pgBouncer.
Практика
1. Включите pg_stat_statements.
2. Соберите топ-20 по времени.
3. Прогоните EXPLAIN на топ-5.
4. Найдите Seq Scan на больших.
5. Добавьте 3 индекса.
6. Замерьте до/после.
7. Проверьте hit rate.
8. Настройте shared_buffers.
Частые ошибки
1. Оптимизировать без метрик:
Не то место
Решение: pg_stat_statements.
2. Игнорировать EXPLAIN:
Гадание
Решение: EXPLAIN ANALYZE.
3. Индекс на всё:
Замедляет запись
Решение: только нужные.
4. Мало shared_buffers:
Низкий hit rate
Решение: 25% RAM.
5. Много соединений:
CPU БД
Решение: pgBouncer.
6. Долгие транзакции:
Блокировки
Решение: короткие.
7. Без мониторинга:
Деградация
Решение: Pulse + pg_stat.
8. Одна БД на всё:
Нет масштаба
Решение: реплики.
Итог
- БД — 70–90% времени.
- Запросы — первый шаг.
- EXPLAIN — обязателен.
- Индексы — где Seq Scan.
- pg_stat_statements — топ.
- Shared buffers — 25% RAM.
- pgBouncer — pooling.
- Метрики — до и после.
- Hit rate > 99%.
- Порядок: запросы → схема → инфра.
Дальше — оптимизация JOIN и подзапросов в Laravel.